diff options
Diffstat (limited to 'xine_sxfe_frontend.c')
-rw-r--r-- | xine_sxfe_frontend.c | 601 |
1 files changed, 601 insertions, 0 deletions
diff --git a/xine_sxfe_frontend.c b/xine_sxfe_frontend.c new file mode 100644 index 00000000..49e2b632 --- /dev/null +++ b/xine_sxfe_frontend.c @@ -0,0 +1,601 @@ +/* + * xine_sxfe_frontend.c: Simple front-end, X11 functions + * + * See the main source file 'xineliboutput.c' for copyright information and + * how to reach the author. + * + * $Id: xine_sxfe_frontend.c,v 1.1 2006-06-03 10:01:18 phintuka Exp $ + * + */ + +#define HAVE_XF86VIDMODE +#define HAVE_XDPMS + +#include <errno.h> +#include <inttypes.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <dlfcn.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <sys/types.h> +#include <unistd.h> +#include <time.h> +#include <pthread.h> +#include <sched.h> +#include <poll.h> +#include <linux/unistd.h> /* gettid() */ + +#include <X11/Xlib.h> +#include <X11/keysym.h> +#include <X11/extensions/XShm.h> +#include <X11/Xutil.h> +#ifdef HAVE_XF86VIDMODE +# include <X11/extensions/xf86vmode.h> +#endif +#ifdef HAVE_XDPMS +# include <X11/extensions/dpms.h> +#endif +#ifdef HAVE_XV_FIELD_ORDER +# include <X11/extensions/Xvlib.h> +#endif + +#ifdef boolean +# define HAVE_BOOLEAN +#endif +#include <jpeglib.h> +#undef boolean + +#include <xine.h> +#ifndef XINE_ENGINE_INTERNAL +# define XINE_ENGINE_INTERNAL +# include <xine/xine_internal.h> +# undef XINE_ENGINE_INTERNAL +#else +# include <xine/xine_internal.h> +#endif +#include <xine/xineutils.h> +#include <xine/input_plugin.h> +#include <xine/plugin_catalog.h> + +#include "xine_input_vdr.h" + +#include "xine_frontend.h" +#include "xine/post.h" + +#define MWM_HINTS_DECORATIONS (1L << 1) +#define PROP_MWM_HINTS_ELEMENTS 5 +typedef struct _mwmhints { + uint32_t flags; + uint32_t functions; + uint32_t decorations; + int32_t input_mode; + uint32_t status; +} MWMHints; + + +/* + * data + */ + +typedef struct sxfe_s { + + /* function pointers */ + frontend_t fe; + void (*update_display_size)(frontend_t*); + + /* xine stuff */ + xine_t *xine; + xine_stream_t *stream; + input_plugin_t *input; + xine_video_port_t *video_port; + xine_audio_port_t *audio_port; + xine_event_queue_t *event_queue; + + post_plugins_t *postplugins; + + char configfile[256]; + + int xine_visual_type; + x11_visual_t vis; + + int pes_buffers; + int aspect; + int cropping; + int scale_video; + int priority; + + + double display_ratio; + + /* frontend */ + int playback_finished; + + /* vdr */ + fe_keypress_f keypress; + + /* X11 */ + Display *display; + int screen; + Window window[2]; + int fullscreen; + int vmode_switch; + int field_order; + char modeline[256]; +#ifdef HAVE_XF86VIDMODE + /* XF86VidMode Extension */ + XF86VidModeModeInfo** XF86_modelines; + int XF86_modelines_count; +#endif + + int completion_event; + + int xpos, ypos; + int width, height; + + Atom wm_del_win; + Atom sxfe_interrupt; + +} fe_t, sxfe_t; + + +/* Common (non-X11/FB) frontend functions */ +#include "xine_frontend.c" + + +static void fe_dest_size_cb (void *data, + int video_width, int video_height, double video_pixel_aspect, + int *dest_width, int *dest_height, double *dest_pixel_aspect) +{ + fe_t *this = (fe_t *)data; + + if (!this) + return; + + *dest_width = this->width; + *dest_height = this->height; + + *dest_pixel_aspect = fe_dest_pixel_aspect(this, video_pixel_aspect, + video_width, video_height); +} + +/* + * sxfe_display_open + * + * connect to X server, create windows + */ + +static int sxfe_display_open(frontend_t *this_gen, int width, int height, int fullscreen, + int modeswitch, char *modeline, int aspect, + fe_keypress_f keyfunc, char *video_port, + int scale_video, int field_order) +{ + sxfe_t *this = (sxfe_t*)this_gen; + + Atom prop; + MWMHints mwmhints; + XSizeHints hint; + double res_h, res_v, aspect_diff; + + if(this->display) + this->fe.fe_display_close(this_gen); + + if(keyfunc) + this->keypress = keyfunc; + + LOGDBG("sxfe_display_open(width=%d, height=%d, fullscreen=%d, display=%s)", + width, height, fullscreen, video_port); + + this->xpos = 0; + this->ypos = 0; + this->width = width; + this->height = height; + + this->fullscreen = fullscreen; + this->vmode_switch = modeswitch; + this->aspect = aspect; + this->cropping = 0; + this->field_order = field_order ? 1 : 0; + this->scale_video = scale_video; + strcpy(this->modeline, modeline); + + /* + * init x11 stuff + */ + + if (!XInitThreads ()) { + LOGERR("sxfe_display_open: XInitThreads failed"); + free(this); + return 0; + } + + if(video_port && strlen(video_port)>3) { + if(!(this->display = XOpenDisplay(video_port))) + LOGERR("sxfe_display_open: failed to connect to X server (%s)", + video_port); + } + if(!this->display) { + if(NULL!=(video_port=getenv("DISPLAY")) && !(this->display = XOpenDisplay(video_port))) + LOGERR("sxfe_display_open: failed to connect to X server (%s)", + video_port); + } + if(!this->display) { + video_port = "127.0.0.1:0.0"; + if(!(this->display = XOpenDisplay(video_port))) + LOGERR("sxfe_display_open: failed to connect to X server (%s)", + video_port); + } + if(!this->display) { + this->display = XOpenDisplay(NULL); + } + if (!this->display) { + LOGERR("sxfe_display_open: failed to connect to X server"); + free(this); + return 0; + } + + XLockDisplay (this->display); + + this->screen = DefaultScreen(this->display); + + /* #warning sxfe_display_open: TODO: switch vmode */ + + /* completion event */ + if (XShmQueryExtension (this->display) == True) { + this->completion_event = XShmGetEventBase (this->display) + ShmCompletion; + } else { + this->completion_event = -1; + } + + if(fullscreen) { + this->width = DisplayWidth(this->display, this->screen); + this->height = DisplayHeight(this->display, this->screen); + } + + /* create and display our video window */ + this->window[0] = XCreateSimpleWindow (this->display, + DefaultRootWindow(this->display), + this->xpos, this->ypos, + this->width, this->height, + 1, 0, 0); + this->window[1] = XCreateSimpleWindow(this->display, XDefaultRootWindow(this->display), + 0, 0, (DisplayWidth(this->display, this->screen)), + (DisplayHeight(this->display, this->screen)), 0, 0, 0); + + hint.flags = USSize | USPosition | PPosition | PSize; + hint.x = 0; + hint.y = 0; + hint.width = DisplayWidth(this->display, this->screen); + hint.height = DisplayHeight(this->display, this->screen); + XSetNormalHints(this->display, this->window[1], &hint); + + /* no border in fullscreen window */ + prop = XInternAtom(this->display, "_MOTIF_WM_HINTS", False); + mwmhints.flags = MWM_HINTS_DECORATIONS; + mwmhints.decorations = 0; + XChangeProperty(this->display, this->window[1], prop, prop, 32, + PropModeReplace, (unsigned char *) &mwmhints, + PROP_MWM_HINTS_ELEMENTS); + + XSelectInput (this->display, this->window[0], + StructureNotifyMask | + ExposureMask | + KeyPressMask ); + XSelectInput (this->display, this->window[1], + StructureNotifyMask | + ExposureMask | + KeyPressMask ); + + XMapRaised (this->display, this->window[this->fullscreen]); + + /* determine display aspect ratio */ + res_h = (DisplayWidth (this->display, this->screen)*1000 + / DisplayWidthMM (this->display, this->screen)); + res_v = (DisplayHeight (this->display, this->screen)*1000 + / DisplayHeightMM (this->display, this->screen)); + this->display_ratio = res_v / res_h; + aspect_diff = this->display_ratio - 1.0; + if ((aspect_diff < 0.01) && (aspect_diff > -0.01)) { + this->display_ratio = 1.0; + } + LOGDBG("Display size : %d x %d mm", + DisplayWidthMM (this->display, this->screen), + DisplayHeightMM (this->display, this->screen)); + LOGDBG(" %d x %d pixels", + DisplayWidth (this->display, this->screen), + DisplayHeight (this->display, this->screen)); + LOGDBG(" %ddpi / %ddpi", + (int)(res_v/1000*25.4), (int)(res_h/1000*25.4)); + LOGDBG("Display ratio: %f/%f = %f", res_v, res_h, this->display_ratio); + + /* we want to get notified if user closes the window */ + this->wm_del_win = XInternAtom(this->display, "WM_DELETE_WINDOW", False); + this->sxfe_interrupt = XInternAtom(this->display, "SXFE_INTERRUPT", False); + + XSetWMProtocols(this->display, this->window[fullscreen], &(this->wm_del_win), 1); + + /* no cursor */ + XDefineCursor(this->display, this->window[0], None); + XDefineCursor(this->display, this->window[1], None); + { + static char bm_no_data[] = { 0,0,0,0, 0,0,0,0 }; + Pixmap bm_no; + Cursor no_ptr; + XColor black, dummy; + bm_no = XCreateBitmapFromData(this->display, this->window[fullscreen], bm_no_data, 8, 8); + XAllocNamedColor(this->display, DefaultColormapOfScreen(DefaultScreenOfDisplay(this->display)), + "black", &black, &dummy); + no_ptr = XCreatePixmapCursor(this->display, bm_no, bm_no, &black, &black, 0, 0); + XDefineCursor(this->display, this->window[0], no_ptr); + XDefineCursor(this->display, this->window[1], no_ptr); + } + + XUnlockDisplay (this->display); + + /* No screen saver */ + /* #warning TODO: suspend --> activate blank screen saver / DPMS display off ? */ + XSetScreenSaver(this->display, 0, 0, DefaultBlanking, DefaultExposures); +#ifdef HAVE_XDPMS + { + int dpms_dummy; + if (DPMSQueryExtension(this->display, &dpms_dummy, &dpms_dummy) && DPMSCapable(this->display)) { +/* DPMSInfo(dpy, &dpms_state, &dpms_on); */ + DPMSDisable(this->display); + } + } +#endif + + this->xine_visual_type = XINE_VISUAL_TYPE_X11; + this->vis.display = this->display; + this->vis.screen = this->screen; + this->vis.d = this->window[this->fullscreen]; + this->vis.dest_size_cb = fe_dest_size_cb; + this->vis.frame_output_cb = fe_frame_output_cb; + this->vis.user_data = this; + + return 1; +} + +/* + * sxfe_display_config + * + * configure windows + */ +static int sxfe_display_config(frontend_t *this_gen, + int width, int height, int fullscreen, + int modeswitch, char *modeline, + int aspect, int scale_video, + int field_order) +{ + sxfe_t *this = (sxfe_t*)this_gen; + + if(this->width != width || this->height != height) { + this->width = width; + this->height = height; + this->fullscreen = fullscreen; + + if(!fullscreen) { + XLockDisplay(this->display); + XResizeWindow(this->display, this->window[0], this->width, this->height); + XUnlockDisplay(this->display); + if(!fullscreen && !this->fullscreen) + xine_gui_send_vo_data(this->stream, XINE_GUI_SEND_DRAWABLE_CHANGED, + (void*) this->window[0]); + } + } + + if(fullscreen) { + this->width = DisplayWidth(this->display, this->screen); + this->height = DisplayHeight(this->display, this->screen); + } + + if(fullscreen != this->fullscreen) { + Window tmp_win; + XLockDisplay(this->display); + XUnmapWindow(this->display, this->window[this->fullscreen]); + this->fullscreen = fullscreen; + XMapRaised(this->display, this->window[this->fullscreen]); + if(!fullscreen) + XResizeWindow(this->display, this->window[0], this->width, this->height); + XSync(this->display, False); + XTranslateCoordinates(this->display, this->window[this->fullscreen], + DefaultRootWindow(this->display), + 0, 0, &this->xpos, &this->ypos, &tmp_win); + XUnlockDisplay(this->display); + xine_gui_send_vo_data(this->stream, XINE_GUI_SEND_DRAWABLE_CHANGED, + (void*) this->window[this->fullscreen]); + } + + if(!modeswitch && strcmp(modeline, this->modeline)) { + strcpy(this->modeline, modeline); + /* #warning TODO - switch vmode */ + } + + this->vmode_switch = modeswitch; + this->aspect = aspect; + this->scale_video = scale_video; +#ifdef HAVE_XV_FIELD_ORDER + if(this->field_order != field_order) { + if(XInternAtom(this->display, "XV_SWAP_FIELDS", True) != None) + XvSetPortAttribute (this->display, 53, + XInternAtom (this->display, "XV_SWAP_FIELDS", False), + field_order); + } +#endif + this->field_order = field_order ? 1 : 0; + + return 1; +} + + +/* + * X event loop + */ + +static void sxfe_interrupt(frontend_t *this_gen) +{ + sxfe_t *this = (sxfe_t*)this_gen; + XClientMessageEvent ev2; + + ev2.type = ClientMessage; + ev2.display = this->display; + ev2.window = this->window[this->fullscreen]; + ev2.message_type = this->sxfe_interrupt; + ev2.format = 32; + + if(!XSendEvent(ev2.display, ev2.window, TRUE, /*KeyPressMask*/0, (XEvent *)&ev2)) + LOGERR("sxfe_interrupt: XSendEvent(ClientMessage) FAILED\n"); + + XFlush(this->display); +} + +static int sxfe_run(frontend_t *this_gen) +{ + sxfe_t *this = (sxfe_t*)this_gen; + + int keep_going = 1; + XEvent event; + + /* poll X server (connection socket). + (XNextEvent will block if no events are queued). + We want to use timeout, blocking for long time usually causes vdr + watchdog to emergency exit ... */ + if (! XPending(this->display)) { + struct pollfd pfd[2]; + pfd[0].fd = ConnectionNumber(this->display); + pfd[0].events = POLLIN; + if(poll(pfd, 1, 500) < 1 || !(pfd[0].revents & POLLIN)) { + return 1; + } + } + + XNextEvent (this->display, &event); + + switch (event.type) { + case Expose: + if (event.xexpose.count == 0) + xine_gui_send_vo_data (this->stream, XINE_GUI_SEND_EXPOSE_EVENT, &event); + break; + + case ConfigureNotify: + { + XConfigureEvent *cev = (XConfigureEvent *) &event; + Window tmp_win; + + this->width = cev->width; + this->height = cev->height; + + if ((cev->x == 0) && (cev->y == 0)) { + XLockDisplay(cev->display); + XTranslateCoordinates(cev->display, cev->window, + DefaultRootWindow(cev->display), + 0, 0, &this->xpos, &this->ypos, &tmp_win); + XUnlockDisplay(cev->display); + } else { + this->xpos = cev->x; + this->ypos = cev->y; + } + break; + } + + case KeyPress: + case KeyRelease: + { + XKeyEvent *kevent = (XKeyEvent *) &event; + KeySym ks; + char *ksname; + char buffer[20]; + int buf_len = 20; + XComposeStatus status; + + if(kevent->keycode) { + XLookupString(kevent, buffer, buf_len, &ks, &status); + ksname = XKeysymToString(ks); +#ifdef FE_STANDALONE + if(/*ks == XK_q || ks == XK_Q ||*/ ks == XK_Escape) + keep_going = 0; + else if(this->input || find_input(this)) + process_xine_keypress(this->input, "XKeySym",ksname, 0, 0); +#else + if(this->keypress) + this->keypress("XKeySym",ksname); +#endif + } + } + break; + + case ClientMessage: + { + XClientMessageEvent *cmessage = (XClientMessageEvent *) &event; + if ( cmessage->message_type == this->sxfe_interrupt ) + LOGDBG("ClientMessage: sxfe_interrupt"); + + if ( cmessage->data.l[0] == this->wm_del_win ) + /* we got a window deletion message from out window manager.*/ + keep_going=0; + } + } + + if (event.type == this->completion_event) + xine_gui_send_vo_data (this->stream, XINE_GUI_SEND_COMPLETION_EVENT, &event); + + return keep_going; +} + +static void sxfe_display_close(frontend_t *this_gen) +{ + sxfe_t *this = (sxfe_t*)this_gen; + + if(this && this->display) { + + if(this->xine) + this->fe.xine_exit(this_gen); + + XLockDisplay(this->display); + XUnmapWindow(this->display, this->window[this->fullscreen]); + XDestroyWindow(this->display, this->window[0]); + XDestroyWindow(this->display, this->window[1]); + XUnlockDisplay(this->display); + XCloseDisplay (this->display); + this->display = NULL; + } +} + +static frontend_t *sxfe_get_frontend(void) +{ + sxfe_t *this = malloc(sizeof(sxfe_t)); + memset(this, 0, sizeof(sxfe_t)); + + this->fe.fe_display_open = sxfe_display_open; + this->fe.fe_display_config = sxfe_display_config; + this->fe.fe_display_close = sxfe_display_close; + + this->fe.xine_init = fe_xine_init; + this->fe.xine_open = fe_xine_open; + this->fe.xine_play = fe_xine_play; + this->fe.xine_stop = fe_xine_stop; + this->fe.xine_close = fe_xine_close; + this->fe.xine_exit = fe_xine_exit; + this->fe.xine_is_finished = fe_is_finished; + + this->fe.fe_run = sxfe_run; + this->fe.fe_interrupt = sxfe_interrupt; + this->fe.fe_free = fe_free; + +#ifndef FE_STANDALONE + this->fe.grab = fe_grab; + this->fe.xine_osd_command = xine_osd_command; + this->fe.xine_control = xine_control; + + this->fe.xine_queue_pes_packet = xine_queue_pes_packet; +#endif /*#ifndef FE_STANDALONE */ + + return (frontend_t*)this; +} + +/* ENTRY POINT */ +const fe_creator_f fe_creator __attribute__((visibility("default"))) = sxfe_get_frontend; + + + |