/* ** Copyright (C) 2002-2004 Daniel Caujolle-Bert ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation; either version 2 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ** */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_XTESTEXTENSION #include #endif #include "common.h" #ifndef XShmGetEventBase extern int XShmGetEventBase(Display *); #endif extern int errno; #define MWM_HINTS_DECORATIONS (1L << 1) #define PROP_MWM_HINTS_ELEMENTS 5 typedef struct { uint32_t flags; uint32_t functions; uint32_t decorations; int32_t input_mode; uint32_t status; } MWMHints; typedef struct { Window video_window; GC gc; Atom XA_DELETE_WINDOW; int x; int y; int video_width; int video_height; double visible_aspect; } x11_drawable_t; typedef struct { Cursor cursors[2]; int visible; } cursors_t; typedef struct { Display *display; int init; int fullscreen; x11_drawable_t x11_drawable[2]; double pixel_aspect; cursors_t cursor; int screen; XColor black, white; Visual *visual; Colormap colormap; int depth; XWMHints *wm_hint; Bool have_xtest; #ifdef HAVE_XTESTEXTENSION KeyCode fake_key; /* Fake key to send */ #endif int fake_rate; pthread_t timed_thread; int frame_width; int frame_height; pthread_mutex_t mutex; } x11_private_t; /* Cursor's bitmap datas */ static unsigned char curs_no[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; static unsigned char curs_yes[] = { 0x00, 0x00, 0x0e, 0x70, 0xfa, 0x5f, 0x0e, 0x70, 0x0a, 0x50, 0x0e, 0x70, 0x0a, 0x50, 0x0e, 0x70, 0xfa, 0x5f, 0x0e, 0x70, 0x0a, 0x50, 0x0e, 0x70, 0x0a, 0x50, 0x0e, 0x70, 0xfa, 0x5f, 0x00, 0x00 }; static unsigned char curs_yes_bg[] = { 0x1f, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1f, 0xf8, 0x1f, 0xf8, 0x1f, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1f, 0xf8, 0x1f, 0xf8, 0x1f, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xfb, 0xdf }; /* * Config callback. */ static void change_fake_rate_cb(void *data, _cfg_entry_t *cfg) { x11_private_t *private = (x11_private_t *)data; private->fake_rate = cfg->num_value; } /* * A thread-safe usecond sleep */ static void usec_sleep(unsigned long usec) { #if HAVE_NANOSLEEP /* nanosleep is prefered on solaris, because it's mt-safe */ struct timespec ts; ts.tv_sec = usec / 1000000; ts.tv_nsec = (usec % 1000000) * 1000; nanosleep(&ts, NULL); #else usleep(usec); #endif } static Bool have_xtestextention(Display *display) { #ifdef HAVE_XTESTEXTENSION int dummy1, dummy2, dummy3, dummy4; return (XTestQueryExtension(display, &dummy1, &dummy2, &dummy3, &dummy4)); #endif return False; } /* * */ void reset_screensaver(toxine_vo_plugin_t *vop) { x11_private_t *private = (x11_private_t *) vop->private; #ifdef HAVE_XTESTEXTENSION if(private->have_xtest == True) { XLockDisplay(private->display); XTestFakeKeyEvent(private->display, private->fake_key, True, CurrentTime); XTestFakeKeyEvent(private->display, private->fake_key, False, CurrentTime); XSync(private->display, False); XUnlockDisplay(private->display); } else #endif { XLockDisplay(private->display); XResetScreenSaver(private->display); XUnlockDisplay(private->display); } } /* * */ static void translate_coords(Display *display, Window win, int *x, int *y) { Window tmp_win; if(display && (win != None) && x && y) { XLockDisplay(display); XTranslateCoordinates(display, win, DefaultRootWindow(display), 0, 0, &*x, &*y, &tmp_win); XUnlockDisplay(display); } } /* * Is window is size match with given args */ static int is_window_visible(Display *display, Window window) { XWindowAttributes wattr; Status status; if((display == NULL) || (window == None)) return -1; XLockDisplay(display); status = XGetWindowAttributes(display, window, &wattr); XUnlockDisplay(display); if((status != BadDrawable) && (status != BadWindow) && (wattr.map_state == IsViewable)) return 1; return 0; } /* * */ static void toggle_fullscreen(toxine_t *tox) { x11_private_t *private = (x11_private_t *) tox->video.cur_plugin->private; pthread_mutex_lock(&private->mutex); XLockDisplay(private->display); XUnmapWindow(private->display, private->x11_drawable[private->fullscreen].video_window); private->fullscreen = !private->fullscreen; XMapRaised(private->display, private->x11_drawable[private->fullscreen].video_window); XMoveResizeWindow (private->display, private->x11_drawable[private->fullscreen].video_window, private->x11_drawable[private->fullscreen].x, private->x11_drawable[private->fullscreen].y, private->x11_drawable[private->fullscreen].video_width, private->x11_drawable[private->fullscreen].video_height); XSync(private->display, False); XUnlockDisplay(private->display); while(!is_window_visible(private->display, private->x11_drawable[private->fullscreen].video_window)) usec_sleep(5000); translate_coords(private->display, private->x11_drawable[private->fullscreen].video_window, &private->x11_drawable[private->fullscreen].x, &private->x11_drawable[private->fullscreen].y); #if 0 printf("FULLSCREEN X %d, Y %d **\n", private->x11_drawable[private->fullscreen].x, private->x11_drawable[private->fullscreen].y); #endif if(tox->video.port && (tox->xine_state & XINE_STREAM)) xine_gui_send_vo_data(tox->stream, XINE_GUI_SEND_DRAWABLE_CHANGED, (void*) private->x11_drawable[private->fullscreen].video_window); pthread_mutex_unlock(&private->mutex); } /* * Update cursor apparence, reflected by cursor.visible value. */ static void update_cursor_visibility(toxine_t *tox) { toxine_vo_plugin_t *vop = (toxine_vo_plugin_t *) tox->video.cur_plugin; x11_private_t *private = (x11_private_t *) vop->private; pthread_mutex_lock(&private->mutex); if(private->x11_drawable[private->fullscreen].video_window != None) { XLockDisplay(private->display); XDefineCursor(private->display, private->x11_drawable[private->fullscreen].video_window, private->cursor.cursors[private->cursor.visible]); XFlush(private->display); XUnlockDisplay(private->display); } pthread_mutex_unlock(&private->mutex); } /* * Thread that just hide the cursor pointer, each 2 secs, if needed. */ static void *timed_loop(void *data) { toxine_t *tox = (toxine_t *) data; toxine_vo_plugin_t *vop = (toxine_vo_plugin_t *) tox->video.cur_plugin; x11_private_t *private = (x11_private_t *) vop->private; int screensaver_timer = 0; while(tox->video.running) { int status; if(tox->xine_state & XINE_STREAM) { if(tox->xine != NULL) { status = xine_get_status(tox->stream); if(status == XINE_STATUS_PLAY) { screensaver_timer++; if(screensaver_timer >= private->fake_rate) { screensaver_timer = 0; reset_screensaver(vop); } } } if(private->cursor.visible) { private->cursor.visible = !private->cursor.visible; update_cursor_visibility(tox); } } xine_usec_sleep(1000000); } pthread_exit(NULL); } /* * */ static int video_window_translate_point(toxine_t *tox, toxine_vo_plugin_t *vop, int gui_x, int gui_y, int *video_x, int *video_y) { x11_private_t *private = (x11_private_t *) vop->private; x11_rectangle_t rect; int xwin, ywin; unsigned int wwin, hwin, bwin, dwin; float xf,yf; float scale, width_scale, height_scale,aspect; Window rootwin; rect.x = gui_x; rect.y = gui_y; rect.w = 0; rect.h = 0; if(tox->video.port && (tox->xine_state & XINE_STREAM) && (xine_gui_send_vo_data(tox->stream, XINE_GUI_SEND_TRANSLATE_GUI_TO_VIDEO, (void*)&rect) != -1)) { /* driver implements gui->video coordinate space translation, use it */ *video_x = rect.x; *video_y = rect.y; return 1; } /* Driver cannot convert gui->video space, fall back to old code... */ if(XGetGeometry(private->display, private->x11_drawable[private->fullscreen].video_window, &rootwin, &xwin, &ywin, &wwin, &hwin, &bwin, &dwin) == BadDrawable) return 0; /* Scale co-ordinate to image dimensions. */ height_scale = (float)private->x11_drawable[private->fullscreen].video_height / (float)hwin; width_scale = (float)private->x11_drawable[private->fullscreen].video_width / (float)wwin; aspect = (float)private->x11_drawable[private->fullscreen].video_width / (float)private->x11_drawable[private->fullscreen].video_height; if(((float)wwin / (float)hwin) < aspect) { scale = width_scale; xf = (float)gui_x * scale; yf = (float)gui_y * scale; //wwin=wwin * scale; hwin = hwin * scale; /* FIXME: The 1.25 should really come from the NAV packets. */ *video_x = xf * 1.25 / aspect; *video_y = yf - ((hwin - private->x11_drawable[private->fullscreen].video_height) / 2); } else { scale = height_scale; xf = (float)gui_x * scale; yf = (float)gui_y * scale; wwin = wwin * scale; /* FIXME: The 1.25 should really come from the NAV packets. */ *video_x = (xf - ((wwin - private->x11_drawable[private->fullscreen].video_width) / 2)) * 1.25 / aspect; *video_y = yf; } return 1; } /* * */ static void video_out_frame_output_cb(void *data, int video_width, int video_height, double video_pixel_aspect, int *dest_x, int *dest_y, int *dest_width, int *dest_height, double *dest_pixel_aspect, int *win_x, int *win_y) { toxine_t *tox = (toxine_t *) data; toxine_vo_plugin_t *vop = (toxine_vo_plugin_t *) tox->video.cur_plugin; x11_private_t *private = (x11_private_t *) vop->private; if(video_pixel_aspect >= private->pixel_aspect) video_width = video_width * video_pixel_aspect / private->pixel_aspect + .5; else video_height = video_height * private->pixel_aspect / video_pixel_aspect + .5; *dest_x = 0; *dest_y = 0; if((private->frame_width == -1) && (private->frame_height == -1)) { private->x11_drawable[private->fullscreen].video_width = video_width; private->x11_drawable[private->fullscreen].video_height = video_height; if(!private->fullscreen) { XLockDisplay(private->display); XResizeWindow(private->display, private->x11_drawable[private->fullscreen].video_window, private->x11_drawable[private->fullscreen].video_width, private->x11_drawable[private->fullscreen].video_height); XUnlockDisplay(private->display); } } private->frame_width = video_width; private->frame_height = video_height; *dest_width = private->x11_drawable[private->fullscreen].video_width; *dest_height = private->x11_drawable[private->fullscreen].video_height; *win_x = private->x11_drawable[private->fullscreen].x; *win_y = private->x11_drawable[private->fullscreen].y; *dest_pixel_aspect = private->x11_drawable[private->fullscreen].visible_aspect; } /* * */ static void video_out_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) { toxine_t *tox = (toxine_t *) data; toxine_vo_plugin_t *vop = (toxine_vo_plugin_t *) tox->video.cur_plugin; x11_private_t *private = (x11_private_t *) vop->private; if(video_pixel_aspect >= private->pixel_aspect) video_width = video_width * video_pixel_aspect / private->pixel_aspect + .5; else video_height = video_height * private->pixel_aspect / video_pixel_aspect + .5; *dest_width = private->x11_drawable[private->fullscreen].video_width; *dest_height = private->x11_drawable[private->fullscreen].video_height; *dest_pixel_aspect = private->x11_drawable[private->fullscreen].visible_aspect; } /* * */ static void video_out_select_visual(toxine_t *tox, toxine_vo_plugin_t *vop) { XVisualInfo *vinfo = (XVisualInfo *) -1; x11_private_t *private = (x11_private_t *) vop->private; XLockDisplay (private->display); if(tox->video.port && (tox->xine_state & XINE_STREAM)) { xine_gui_send_vo_data(tox->stream, XINE_GUI_SEND_SELECT_VISUAL, (void *)&vinfo); if(vinfo != (XVisualInfo *) -1) { if(!vinfo) { fprintf (stderr, "videowin: output driver cannot select a working visual\n"); exit(1); } private->visual = vinfo->visual; private->depth = vinfo->depth; } } XUnlockDisplay (private->display); } /* * */ static void video_window_init(toxine_t *tox) { toxine_vo_plugin_t *vop = (toxine_vo_plugin_t *) tox->video.cur_plugin; Pixmap cursor_no, cursor_yes, cursor_yes_bg; char *display_name = ":0.0"; XColor dummy; x11_private_t *private = (x11_private_t *) vop->private; static char *window_title = "toxine video output"; XSizeHints hint; XSetWindowAttributes attr; Atom XA_NO_BORDER, XA_WIN_LAYER; XEvent xev; XGCValues xgcv; MWMHints mwmhints; XWMHints *wm_hint; long propvalue[1]; char *drivername; static char *videoout_modes[] = { "letterboxed tv", "widescreen tv", "overlay", NULL }; int mode = 2; if(!XInitThreads()) { perr("XInitThreads failed - looks like you don't have a " "thread-safe xlib.\n"); exit(1); } private->fake_rate = xine_config_register_num(tox->xine, "tox.fake_rate", 10, "time between two fake key events (in s)", "Each x seconds, this plugin fake a key event" ", which prevent screensaver activation.", 0, change_fake_rate_cb, (void*)private); if(getenv("DISPLAY")) display_name = getenv("DISPLAY"); private->display = XOpenDisplay(display_name); private->have_xtest = have_xtestextention(private->display); pthread_mutex_init(&private->mutex, NULL); XLockDisplay (private->display); private->init = 1; private->fullscreen = 0; private->screen = DefaultScreen(private->display); private->visual = DefaultVisual(private->display, private->screen); private->colormap = DefaultColormap(private->display, private->screen); private->depth = DefaultDepth(private->display, private->screen); #ifdef HAVE_XTESTEXTENSION private->fake_key = XKeysymToKeycode(private->display, XK_Shift_L); #endif video_out_select_visual(tox, vop); XAllocNamedColor(private->display, DefaultColormap(private->display, private->screen), "black", &private->black, &dummy); XAllocNamedColor(private->display, DefaultColormap(private->display, private->screen), "white", &private->white, &dummy); private->wm_hint = XAllocWMHints(); if(!private->wm_hint) { printf ("XAllocWMHints failed\n"); exit (1); } private->wm_hint->input = True; private->wm_hint->initial_state = NormalState; private->wm_hint->flags = InputHint | StateHint; private->x11_drawable[private->fullscreen].video_width = 720; private->x11_drawable[private->fullscreen].video_height = 576; private->frame_width = -1; private->frame_height = -1; /* Setup windowed window */ hint.x = 0; hint.y = 0; hint.width = private->x11_drawable[private->fullscreen].video_width; hint.height = private->x11_drawable[private->fullscreen].video_height; hint.flags = PPosition | PSize; attr.background_pixel = private->black.pixel; attr.border_pixel = private->black.pixel; attr.colormap = private->colormap; attr.override_redirect = False; private->x11_drawable[private->fullscreen].video_window = XCreateWindow(private->display, (DefaultRootWindow(private->display)), hint.x, hint.y, hint.width, hint.height, 0, private->depth, CopyFromParent, private->visual, CWBackPixel | CWBorderPixel | CWOverrideRedirect | CWColormap, &attr); /* Setup fullscreen window.*/ private->fullscreen = 1; private->x11_drawable[private->fullscreen].video_width = DisplayWidth(private->display, private->screen); private->x11_drawable[private->fullscreen].video_height = DisplayHeight(private->display, private->screen); private->x11_drawable[private->fullscreen].video_window = XCreateWindow(private->display, (DefaultRootWindow(private->display)), hint.x, hint.y, private->x11_drawable[private->fullscreen].video_width, private->x11_drawable[private->fullscreen].video_height, 0, private->depth, CopyFromParent, private->visual, CWBackPixel | CWBorderPixel | CWOverrideRedirect | CWColormap, &attr); /* * Set fullscreen window on top of everythings. */ XA_WIN_LAYER = XInternAtom(private->display, "_WIN_LAYER", False); propvalue[0] = 10; XChangeProperty(private->display, private->x11_drawable[private->fullscreen].video_window, XA_WIN_LAYER, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)propvalue, 1); /* * No borders for fullscreen window. */ XA_NO_BORDER = XInternAtom(private->display, "_MOTIF_WM_HINTS", False); mwmhints.flags = MWM_HINTS_DECORATIONS; mwmhints.decorations = 0; XChangeProperty(private->display, private->x11_drawable[private->fullscreen].video_window, XA_NO_BORDER, XA_NO_BORDER, 32, PropModeReplace, (unsigned char *) &mwmhints, PROP_MWM_HINTS_ELEMENTS); private->fullscreen = 0; /* * Set wm hints, input and few things for both windows. */ do { XSetWMHints(private->display, private->x11_drawable[private->fullscreen].video_window, private->wm_hint); /* Tell other applications about window */ XSetStandardProperties(private->display, private->x11_drawable[private->fullscreen].video_window, window_title, window_title, None, NULL, 0, 0); XSetWMNormalHints (private->display, private->x11_drawable[private->fullscreen].video_window, &hint); XSelectInput(private->display, private->x11_drawable[private->fullscreen].video_window, StructureNotifyMask | ExposureMask | KeyPressMask | ButtonPressMask | PointerMotionMask); private->x11_drawable[private->fullscreen].XA_DELETE_WINDOW = XInternAtom(private->display, "WM_DELETE_WINDOW", False); XSetWMProtocols(private->display, private->x11_drawable[private->fullscreen].video_window, &private->x11_drawable[private->fullscreen].XA_DELETE_WINDOW, 1); private->x11_drawable[private->fullscreen].gc = XCreateGC(private->display, private->x11_drawable[private->fullscreen].video_window, 0L, &xgcv); wm_hint = XAllocWMHints(); if (wm_hint != NULL) { wm_hint->input = True; wm_hint->flags = InputHint; XSetWMHints(private->display, private->x11_drawable[private->fullscreen].video_window, wm_hint); XFree(wm_hint); } private->fullscreen++; } while(private->fullscreen < 2); /* Start in windowed mode. */ private->fullscreen = 0; /* Create mouse cursors */ cursor_no = XCreateBitmapFromData(private->display, (DefaultRootWindow(private->display)), curs_no, 8, 8); cursor_yes = XCreateBitmapFromData(private->display, (DefaultRootWindow(private->display)), curs_yes, 16, 16); cursor_yes_bg = XCreateBitmapFromData(private->display, (DefaultRootWindow(private->display)), curs_yes_bg, 16, 16); private->cursor.cursors[0] = XCreatePixmapCursor(private->display, cursor_no, cursor_no, &private->black, &private->black, 0, 0); private->cursor.cursors[1] = XCreatePixmapCursor(private->display, cursor_yes, cursor_yes_bg, &private->black, &private->white, 0, 0); private->cursor.visible = 1; update_cursor_visibility(tox); XFreePixmap(private->display, cursor_no); XFreePixmap(private->display, cursor_yes); XFreePixmap(private->display, cursor_yes_bg); /* Map window. */ XSetTransientForHint(private->display, private->x11_drawable[private->fullscreen].video_window, None); XMapRaised(private->display, private->x11_drawable[private->fullscreen].video_window); /* Wait for map. */ do { XMaskEvent(private->display, StructureNotifyMask, &xev) ; } while ((xev.type != MapNotify) || (xev.xmap.event != private->x11_drawable[private->fullscreen].video_window)); XFlush(private->display); XUnlockDisplay (private->display); { double res_h, res_v; x11_visual_t vis; vis.display = private->display; vis.screen = private->screen; vis.d = private->x11_drawable[private->fullscreen].video_window; res_h = (DisplayWidth(private->display, private->screen) * 1000 / DisplayWidthMM(private->display, private->screen)); res_v = (DisplayHeight(private->display, private->screen) * 1000 / DisplayHeightMM(private->display, private->screen)); private->pixel_aspect = res_h / res_v; vis.dest_size_cb = video_out_dest_size_cb; vis.frame_output_cb = video_out_frame_output_cb; vis.user_data = (void *)tox; private->x11_drawable[0].visible_aspect = private->pixel_aspect; private->x11_drawable[1].visible_aspect = private->pixel_aspect; /* DXR3 Overlay specific code */ if(!strcasecmp(tox->video.name, "dxr3_ov")) { /* First, we have to initialize the config entry */ (void *) xine_config_register_enum(tox->xine, "dxr3.videoout_mode", 0, videoout_modes, "Dxr3: videoout mode (tv or overlay)", NULL, 0, NULL, NULL); /* Then, force it to Overlay mode */ toxine_config_update_num(tox, "dxr3.videoout_mode", mode); /* Force saving */ toxine_config_save(tox); /* Reload config file now */ toxine_config_load(tox); xine_strdupa(drivername, "dxr3"); } else xine_strdupa(drivername, tox->video.name); tox->video.port = xine_open_video_driver(tox->xine, drivername, XINE_VISUAL_TYPE_X11, (void *)&vis); if(tox->video.port == NULL) { fprintf(stderr, "xine_open_video_driver() failed to load '%s' driver.\n", drivername); exit(1); } else pinfo("video driver '%s' successfully loaded.\n", drivername); } translate_coords(private->display, private->x11_drawable[private->fullscreen].video_window, &private->x11_drawable[private->fullscreen].x, &private->x11_drawable[private->fullscreen].y); { pthread_attr_t pth_attrs; struct sched_param pth_params; pthread_attr_init(&pth_attrs); pthread_attr_getschedparam(&pth_attrs, &pth_params); pth_params.sched_priority = sched_get_priority_min(SCHED_OTHER); pthread_attr_setschedparam(&pth_attrs, &pth_params); pthread_create(&private->timed_thread, &pth_attrs, timed_loop, (void *)tox); } } /* * */ static void init_video_out(toxine_t *tox) { video_window_init (tox); } /* * */ static void video_window_handle_event(XEvent *xev, toxine_t *tox) { toxine_vo_plugin_t *vop = (toxine_vo_plugin_t *) tox->video.cur_plugin; x11_private_t *private = (x11_private_t *) vop->private; switch(xev->type) { case KeyPress: { XKeyEvent kevent; KeySym ksym; char kbuf[256]; int len; if((!private->cursor.visible) #ifdef HAVE_XTESTEXTENSION && (xev->xkey.keycode != private->fake_key) #endif ) { private->cursor.visible = !private->cursor.visible; update_cursor_visibility(tox); } kevent = xev->xkey; XLockDisplay(private->display); len = XLookupString(&kevent, kbuf, sizeof(kbuf), &ksym, NULL); XUnlockDisplay(private->display); switch (ksym) { /* 1:1 */ case XK_1: case XK_KP_1: pthread_mutex_lock(&private->mutex); private->x11_drawable[0].video_width = private->frame_width; private->x11_drawable[0].video_height = private->frame_height; if(!private->fullscreen) { XLockDisplay(private->display); XResizeWindow(private->display, private->x11_drawable[private->fullscreen].video_window, private->x11_drawable[private->fullscreen].video_width, private->x11_drawable[private->fullscreen].video_height); XUnlockDisplay(private->display); } pthread_mutex_unlock(&private->mutex); break; /* Fullscreen toggle */ case XK_f: case XK_F: toggle_fullscreen(tox); break; case XK_BackSpace: /* speed == * 1 */ if(tox->xine_state & XINE_STREAM) { if((xine_get_param(tox->stream, XINE_PARAM_SPEED)) != XINE_SPEED_NORMAL) xine_set_param(tox->stream, XINE_PARAM_SPEED, XINE_SPEED_NORMAL); } break; case XK_Up: /* speed++ */ if(tox->xine_state & XINE_STREAM) { if((xine_get_param(tox->stream, XINE_PARAM_SPEED)) < XINE_SPEED_FAST_4) { if((xine_get_param(tox->stream, XINE_PARAM_SPEED)) > XINE_SPEED_PAUSE) xine_set_param(tox->stream, XINE_PARAM_SPEED, ((xine_get_param(tox->stream, XINE_PARAM_SPEED)) * 2)); else xine_set_param(tox->stream, XINE_PARAM_SPEED, XINE_SPEED_SLOW_4); } } break; case XK_Down: /* speed-- */ if(tox->xine_state & XINE_STREAM) { if((xine_get_param(tox->stream, XINE_PARAM_SPEED)) > XINE_SPEED_PAUSE) xine_set_param(tox->stream, XINE_PARAM_SPEED, ((xine_get_param(tox->stream, XINE_PARAM_SPEED)) / 2)); } break; case XK_space: if(tox->xine_state & XINE_STREAM) { if(xine_get_param(tox->stream, XINE_PARAM_SPEED) != XINE_SPEED_PAUSE) xine_set_param(tox->stream, XINE_PARAM_SPEED, XINE_SPEED_PAUSE); else xine_set_param(tox->stream, XINE_PARAM_SPEED, XINE_SPEED_NORMAL); } break; } } break; case MotionNotify: { XMotionEvent *mevent = (XMotionEvent *) xev; xine_event_t event; xine_input_data_t input; int x, y; if(!private->cursor.visible) { private->cursor.visible = !private->cursor.visible; update_cursor_visibility(tox); } /* printf("Mouse event:mx=%d my=%d\n",mevent->x, mevent->y); */ if((tox->xine_state & XINE_STREAM) && (video_window_translate_point(tox, vop, mevent->x, mevent->y, &x, &y))) { event.type = XINE_EVENT_INPUT_MOUSE_MOVE; event.stream = tox->stream; event.data = &input; event.data_length = sizeof(input); gettimeofday(&event.tv, NULL); input.button = 0; /* No buttons, just motion. */ input.x = x; input.y = y; xine_event_send(tox->stream, &event); } } break; case ButtonPress: { XButtonEvent *bevent = (XButtonEvent *) xev; xine_event_t event; xine_input_data_t input; int x, y; if(!private->cursor.visible) { private->cursor.visible = !private->cursor.visible; update_cursor_visibility(tox); } if(bevent->button == Button1) { if((tox->xine_state & XINE_STREAM) && (video_window_translate_point(tox, vop, bevent->x, bevent->y, &x, &y))) { event.type = XINE_EVENT_INPUT_MOUSE_BUTTON; event.stream = tox->stream; event.data = &input; event.data_length = sizeof(input); gettimeofday(&event.tv, NULL); input.button = 1; input.x = x; input.y = y; xine_event_send(tox->stream, &event); } } } break; case Expose: { XExposeEvent * xevent = (XExposeEvent *) xev; if (xevent->count == 0) { if((xev->xany.window == private->x11_drawable[private->fullscreen].video_window) && tox->video.port && (tox->xine_state & XINE_STREAM)) xine_gui_send_vo_data(tox->stream, XINE_GUI_SEND_EXPOSE_EVENT, (void *)xev); } } break; case ConfigureNotify: pthread_mutex_lock(&private->mutex); if(xev->xany.window == private->x11_drawable[private->fullscreen].video_window) { XConfigureEvent *cev = (XConfigureEvent *) xev; private->x11_drawable[private->fullscreen].video_width = cev->width; private->x11_drawable[private->fullscreen].video_height = cev->height; if((cev->x == 0) && (cev->y == 0)) { translate_coords(private->display, private->x11_drawable[private->fullscreen].video_window, &private->x11_drawable[private->fullscreen].x, &private->x11_drawable[private->fullscreen].y); } else { private->x11_drawable[private->fullscreen].x = cev->x; private->x11_drawable[private->fullscreen].y = cev->y; } #if 0 printf("CONFIGURE X %d, Y %d ** w %d, h %d ** 0x%lx\n", private->x11_drawable[private->fullscreen].x, private->x11_drawable[private->fullscreen].y, private->x11_drawable[private->fullscreen].video_width, private->x11_drawable[private->fullscreen].video_height, private->x11_drawable[private->fullscreen].video_window); #endif } pthread_mutex_unlock(&private->mutex); break; } } /* * */ static Bool is_not_completion(Display *display, XEvent *event, XPointer arg) { return True; } static void *event_loop(void *data) { toxine_t *tox = (toxine_t *) data; toxine_vo_plugin_t *vop = (toxine_vo_plugin_t *) tox->video.cur_plugin; x11_private_t *private = (x11_private_t *) vop->private; XEvent myevent; int xconnection; Bool got_xevent; struct timeval tv; fd_set r; pthread_detach(pthread_self()); XLockDisplay(private->display); xconnection = ConnectionNumber(private->display); XUnlockDisplay(private->display); while(tox->video.running) { FD_ZERO(&r); FD_SET(xconnection, &r); tv.tv_sec = 0; tv.tv_usec = 33000; select(xconnection + 1, &r, 0, 0, &tv); XLockDisplay(private->display); got_xevent = XCheckIfEvent(private->display, &myevent, is_not_completion, (XPointer) NULL); XUnlockDisplay(private->display); /* While event isn't completion */ while(got_xevent == True) { video_window_handle_event(&myevent, tox); XLockDisplay(private->display); got_xevent = XCheckIfEvent(private->display, &myevent, is_not_completion, (XPointer) NULL); XUnlockDisplay(private->display); } } pthread_exit(NULL); } static void deinit_video_out(toxine_t *tox) { toxine_vo_plugin_t *vop = (toxine_vo_plugin_t *) tox->video.cur_plugin; x11_private_t *private = (x11_private_t *) vop->private; XWindowAttributes wattr; Status status; pthread_mutex_lock(&private->mutex); pthread_join(private->timed_thread, NULL); xine_close_video_driver(tox->xine, tox->video.port); tox->video.port = NULL; XLockDisplay(private->display); XUnmapWindow(private->display, private->x11_drawable[private->fullscreen].video_window); /* Waiting til' window's unmapped. */ do { status = XGetWindowAttributes(private->display, private->x11_drawable[private->fullscreen].video_window, &wattr); } while((status != BadDrawable) && (status != BadWindow) && (wattr.map_state == IsViewable)); XFreeCursor(private->display, private->cursor.cursors[0]); XFreeCursor(private->display, private->cursor.cursors[1]); XFreeGC(private->display, private->x11_drawable[private->fullscreen].gc); XDestroyWindow(private->display, private->x11_drawable[private->fullscreen].video_window); private->fullscreen = !private->fullscreen; XFreeGC(private->display, private->x11_drawable[private->fullscreen].gc); XDestroyWindow(private->display, private->x11_drawable[private->fullscreen].video_window); XUnlockDisplay(private->display); XCloseDisplay(private->display); pthread_mutex_unlock(&private->mutex); pthread_mutex_destroy(&private->mutex); private->init = 0; } /* * */ static void release_video_out(toxine_t *tox) { toxine_vo_plugin_t *vop = (toxine_vo_plugin_t *) tox->video.last_plugin; x11_private_t *private = (x11_private_t *) vop->private; if(private->init) deinit_video_out(tox); free(private); free(vop); } /* * */ static const char *get_identifier(void) { return "X11 video out"; } /* * */ static char *_vo_x11_names[] = { "Xv", "XShm", "dxr3_ov", "vidix", NULL }; static const char **get_names(void) { return (const char **)_vo_x11_names; } #if 0 /* * */ static uint32_t get_capabilities(toxine_t *tox) { if(tox->video.port) return tox->video.port->get_capabilities(tox->video.port); return 0; } /* * */ static int get_property(toxine_t *tox, int property) { if(tox->video.port) return tox->video.port->get_property(tox->video.port, property); return 0; } /* * */ static void get_property_min_max(toxine_t *tox, int property, int *min, int *max) { if(tox->video.port) tox->video.port->get_property_min_max(tox->video.port, property, min, max); } /* * */ static int set_property(toxine_t *tox, int property, int value) { if(tox->video.port) return tox->video.port->set_property(tox->video.port, property, value); return value; } #endif /* * */ static const char *get_help(void) { return "Video out plugin supports Xv, Xshm, DXR3/H+ overlay and Vidix drivers.\n" "\tShortcuts are:\n" "\t1\t\t: Stream size 1:1,\n" "\tf, F\t\t: Fullscreen toggle,\n" "\t\t\t: Pause/Resume toggle,\n" "\t\t\t: Increase playback speed,\n" "\t\t\t: Decrease playback speed,\n" "\t\t: Reset playback speed.\n"; } /* * */ static int is_fullscreen(toxine_t *tox) { toxine_vo_plugin_t *vop = (toxine_vo_plugin_t *) tox->video.cur_plugin; x11_private_t *private = (x11_private_t *) vop->private; return (private->fullscreen ? 1 : 0); } /* * */ static void fullscreen(toxine_t *tox) { toggle_fullscreen(tox); update_cursor_visibility(tox); } /* * */ toxine_vo_plugin_t *toxine_load_vo_plugin(toxine_t *tox) { toxine_vo_plugin_t *x11; x11_private_t *private; x11 = (toxine_vo_plugin_t *) xine_xmalloc(sizeof(toxine_vo_plugin_t)); private = (x11_private_t *) xine_xmalloc(sizeof(x11_private_t)); private->init = 0; x11->video_out_init = init_video_out; x11->video_out_event_loop = event_loop; x11->video_out_deinit = deinit_video_out; x11->video_out_release = release_video_out; x11->get_identifier = get_identifier; x11->get_names = get_names; x11->get_help = get_help; x11->receive_xine_event = NULL; x11->is_fullscreen = is_fullscreen; x11->fullscreen = fullscreen; x11->private = (void *) private; return x11; }