/* * AutoSpace Window System for Linux/Win32 v0.85 * written by pontscho/fresh!mindworkz * * This file is part of MPlayer. * * MPlayer 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. * * MPlayer 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 MPlayer; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include #include #include #include #include #include #include #include #include #include #include "gui/interface.h" #include "config.h" #include "libvo/x11_common.h" #include "libvo/video_out.h" #include "cpudetect.h" #include "libswscale/swscale.h" #include "libavutil/imgutils.h" #include "libmpcodecs/vf_scale.h" #include "mp_core.h" #include "mp_msg.h" #include "help_mp.h" #include "mplayer.h" #include "mpbswap.h" #include "osdep/timer.h" #include "ws.h" #include "wsxdnd.h" #ifdef CONFIG_XSHAPE #include #endif #ifdef CONFIG_XF86VM #include #endif #include #ifdef HAVE_SHM #include #endif #define MOUSEHIDE_DELAY 1000 // in milliseconds static wsTWindow *mouse_win; static unsigned int mouse_time; typedef struct { unsigned long flags; unsigned long functions; unsigned long decorations; long input_mode; unsigned long status; } MotifWmHints; Atom wsMotifHints; int wsMaxX = 0; // Screen width. int wsMaxY = 0; // Screen height. int wsOrgX = 0; // Screen origin x. int wsOrgY = 0; // Screen origin y. Display *wsDisplay; int wsScreen; Window wsRootWin; XEvent wsEvent; int wsWindowDepth; GC wsHGC; MotifWmHints wsMotifWmHints; Atom wsTextProperlyAtom = None; int wsLayer = 0; int wsDepthOnScreen = 0; int wsRedMask = 0; int wsGreenMask = 0; int wsBlueMask = 0; int wsOutMask = 0; int wsNonNativeOrder = 0; Bool wsTrue = True; #define wsWLCount 5 wsTWindow *wsWindowList[wsWLCount] = { NULL, NULL, NULL, NULL, NULL }; unsigned long wsKeyTable[512]; int wsUseXShm = True; int wsUseXShape = True; static int wsSearch(Window win) { int i; for (i = 0; i < wsWLCount; i++) if (wsWindowList[i] && wsWindowList[i]->WindowID == win) return i; return -1; } /* --- */ #define PACK_RGB16(r, g, b, pixel) pixel = (b >> 3); \ pixel <<= 6; \ pixel |= (g >> 2); \ pixel <<= 5; \ pixel |= (r >> 3) #define PACK_RGB15(r, g, b, pixel) pixel = (b >> 3); \ pixel <<= 5; \ pixel |= (g >> 3); \ pixel <<= 5; \ pixel |= (r >> 3) struct SwsContext *sws_ctx = NULL; enum PixelFormat out_pix_fmt = PIX_FMT_NONE; /* --- */ #define MWM_HINTS_FUNCTIONS (1L << 0) #define MWM_HINTS_DECORATIONS (1L << 1) #define MWM_HINTS_INPUT_MODE (1L << 2) #define MWM_HINTS_STATUS (1L << 3) #define MWM_FUNC_ALL (1L << 0) #define MWM_FUNC_RESIZE (1L << 1) #define MWM_FUNC_MOVE (1L << 2) #define MWM_FUNC_MINIMIZE (1L << 3) #define MWM_FUNC_MAXIMIZE (1L << 4) #define MWM_FUNC_CLOSE (1L << 5) #define MWM_DECOR_ALL (1L << 0) #define MWM_DECOR_BORDER (1L << 1) #define MWM_DECOR_RESIZEH (1L << 2) #define MWM_DECOR_TITLE (1L << 3) #define MWM_DECOR_MENU (1L << 4) #define MWM_DECOR_MINIMIZE (1L << 5) #define MWM_DECOR_MAXIMIZE (1L << 6) #define MWM_INPUT_MODELESS 0 #define MWM_INPUT_PRIMARY_APPLICATION_MODAL 1 #define MWM_INPUT_SYSTEM_MODAL 2 #define MWM_INPUT_FULL_APPLICATION_MODAL 3 #define MWM_INPUT_APPLICATION_MODAL MWM_INPUT_PRIMARY_APPLICATION_MODAL #define MWM_TEAROFF_WINDOW (1L << 0) void wsWindowDecoration(wsTWindow *win, Bool decor) { wsMotifHints = XInternAtom(wsDisplay, "_MOTIF_WM_HINTS", 0); if (wsMotifHints == None) return; memset(&wsMotifWmHints, 0, sizeof(MotifWmHints)); wsMotifWmHints.flags = MWM_HINTS_FUNCTIONS | MWM_HINTS_DECORATIONS; if (decor) { wsMotifWmHints.functions = MWM_FUNC_MOVE | MWM_FUNC_CLOSE | MWM_FUNC_MINIMIZE | MWM_FUNC_MAXIMIZE | MWM_FUNC_RESIZE; wsMotifWmHints.decorations = MWM_DECOR_ALL; } XChangeProperty(wsDisplay, win->WindowID, wsMotifHints, wsMotifHints, 32, PropModeReplace, (unsigned char *)&wsMotifWmHints, 5); } // ---------------------------------------------------------------------------------------------- // Init X Window System. // ---------------------------------------------------------------------------------------------- static int wsErrorHandler(Display *dpy, XErrorEvent *Event) { char type[128]; XGetErrorText(dpy, Event->error_code, type, 128); fprintf(stderr, "[ws] Error in display.\n"); fprintf(stderr, "[ws] Error code: %d ( %s )\n", Event->error_code, type); fprintf(stderr, "[ws] Request code: %d\n", Event->request_code); fprintf(stderr, "[ws] Minor code: %d\n", Event->minor_code); fprintf(stderr, "[ws] Modules: %s\n", current_module ? current_module : "(NULL)"); return 0; } /** * @brief Update screen width, screen height and screen origin x and y * from xinerama information. * * Set wsOrgX, wsOrgY, wsMaxX and wsMaxY as well as * win->X, win->Y, win->Width and win->Height. * * @param win pointer to a ws window structure or NULL */ static void wsUpdateXineramaInfo(wsTWindow *win) { if (win) { vo_dx = win->X; vo_dy = win->Y; vo_dwidth = win->Width; vo_dheight = win->Height; } vo_screenwidth = wsMaxX; vo_screenheight = wsMaxY; update_xinerama_info(); wsMaxX = vo_screenwidth; wsMaxY = vo_screenheight; wsOrgX = xinerama_x; wsOrgY = xinerama_y; if (win) { win->X = wsOrgX; win->Y = wsOrgY; win->Width = wsMaxX; win->Height = wsMaxY; } } void wsXInit(Display *mDisplay) { int eventbase; int errorbase; // NOTE TO MYSELF: Use global mDisplay, get rid of wsDisplay. wsDisplay = mDisplay; XSetErrorHandler(wsErrorHandler); /* enable DND atoms */ wsXDNDInitialize(); { /* on remote display XShm will be disabled - LGB */ char *dispname = DisplayString(wsDisplay); int localdisp = 1; if (dispname && *dispname != ':') { localdisp = 0; wsUseXShm = False; } mp_msg(MSGT_GPLAYER, MSGL_DBG2, "[ws] display name: %s => %s display.\n", dispname, localdisp ? "local" : "REMOTE"); if (!localdisp) mp_msg(MSGT_GPLAYER, MSGL_INFO, MSGTR_WS_RemoteDisplay); } #ifdef HAVE_SHM if (!XShmQueryExtension(wsDisplay)) #endif wsUseXShm = False; if (!wsUseXShm) mp_msg(MSGT_GPLAYER, MSGL_INFO, MSGTR_WS_NoXshm); #ifdef CONFIG_XSHAPE if (!XShapeQueryExtension(wsDisplay, &eventbase, &errorbase)) #endif wsUseXShape = False; if (!wsUseXShape) mp_msg(MSGT_GPLAYER, MSGL_WARN, MSGTR_WS_NoXshape); XSynchronize(wsDisplay, True); wsScreen = DefaultScreen(wsDisplay); wsRootWin = RootWindow(wsDisplay, wsScreen); #ifdef CONFIG_XF86VM { int clock; XF86VidModeModeLine modeline; XF86VidModeGetModeLine(wsDisplay, wsScreen, &clock, &modeline); wsMaxX = modeline.hdisplay; wsMaxY = modeline.vdisplay; } #endif { wsOrgX = wsOrgY = 0; if (!wsMaxX) wsMaxX = DisplayWidth(wsDisplay, wsScreen); if (!wsMaxY) wsMaxY = DisplayHeight(wsDisplay, wsScreen); } wsUpdateXineramaInfo(NULL); wsGetDepthOnScreen(); mp_msg(MSGT_GPLAYER, MSGL_DBG2, "[ws] Screen depth: %d\n", wsDepthOnScreen); mp_msg(MSGT_GPLAYER, MSGL_DBG2, "[ws] size: %dx%d\n", wsMaxX, wsMaxY); #ifdef CONFIG_XINERAMA mp_msg(MSGT_GPLAYER, MSGL_DBG2, "[ws] origin: +%d+%d\n", wsOrgX, wsOrgY); #endif mp_msg(MSGT_GPLAYER, MSGL_DBG2, "[ws] red mask: 0x%x\n", wsRedMask); mp_msg(MSGT_GPLAYER, MSGL_DBG2, "[ws] green mask: 0x%x\n", wsGreenMask); mp_msg(MSGT_GPLAYER, MSGL_DBG2, "[ws] blue mask: 0x%x\n", wsBlueMask); #ifdef HAVE_SHM if (wsUseXShm) { int minor, major, shp; XShmQueryVersion(wsDisplay, &major, &minor, &shp); mp_msg(MSGT_GPLAYER, MSGL_DBG2, "[ws] XShm version is %d.%d\n", major, minor); } #endif #ifdef CONFIG_XSHAPE if (wsUseXShape) { int minor, major; XShapeQueryVersion(wsDisplay, &major, &minor); mp_msg(MSGT_GPLAYER, MSGL_DBG2, "[ws] XShape version is %d.%d\n", major, minor); } #endif wsOutMask = wsGetOutMask(); switch (wsOutMask) { case wsRGB32: out_pix_fmt = PIX_FMT_RGB32; break; case wsBGR32: out_pix_fmt = PIX_FMT_BGR32; break; case wsRGB24: out_pix_fmt = PIX_FMT_RGB24; break; case wsBGR24: out_pix_fmt = PIX_FMT_BGR24; break; case wsRGB16: out_pix_fmt = PIX_FMT_RGB565; break; case wsBGR16: out_pix_fmt = PIX_FMT_BGR565; break; case wsRGB15: out_pix_fmt = PIX_FMT_RGB555; break; case wsBGR15: out_pix_fmt = PIX_FMT_BGR555; break; } } /** * @brief Calculate and store the x and y position for a window. * * @param win pointer to a ws window structure * @param x x position of the window (real/absolute or mock) * @param y y position of the window (real/absolute or mock) * @param width width of the area to place the window in * @param height height of the area to place the window in */ static void wsWindowPosition(wsTWindow *win, int x, int y, int width, int height) { switch (x) { case -1: win->X = wsOrgX + (wsMaxX - width) / 2; break; case -2: win->X = wsOrgX + wsMaxX - width; break; default: win->X = x; break; } switch (y) { case -1: win->Y = wsOrgY + (wsMaxY - height) / 2; break; case -2: win->Y = wsOrgY + wsMaxY - height; break; default: win->Y = y; break; } } // ---------------------------------------------------------------------------------------------- // Create window. // X,Y : window position // wX,wY : size of window // bW : border width // cV : visible mouse cursor on window // D : visible frame, title, etc. // sR : screen ratio // ---------------------------------------------------------------------------------------------- XClassHint wsClassHint; XTextProperty wsTextProperty; Window LeaderWindow; void wsCreateWindow(wsTWindow *win, int X, int Y, int wX, int hY, int bW, int cV, unsigned char D, char *label) { int depth; win->Property = D; if (D & wsShowFrame) win->Decorations = True; wsHGC = DefaultGC(wsDisplay, wsScreen); wsWindowPosition(win, X, Y, wX, hY); win->Width = wX; win->Height = hY; win->OldX = win->X; win->OldY = win->Y; win->OldWidth = win->Width; win->OldHeight = win->Height; /* Border size for window. */ win->BorderWidth = bW; /* Hide Mouse Cursor */ win->wsCursor = None; win->wsMouseEventType = cV; win->wsCursorData[0] = 0; win->wsCursorPixmap = XCreateBitmapFromData(wsDisplay, wsRootWin, win->wsCursorData, 1, 1); if (!(cV & wsShowMouseCursor)) win->wsCursor = XCreatePixmapCursor(wsDisplay, win->wsCursorPixmap, win->wsCursorPixmap, &win->wsColor, &win->wsColor, 0, 0); depth = vo_find_depth_from_visuals(wsDisplay, wsScreen, NULL); if (depth < 15) { mp_msg(MSGT_GPLAYER, MSGL_FATAL, MSGTR_WS_ColorDepthTooLow); mplayer(MPLAYER_EXIT_GUI, EXIT_ERROR, 0); } XMatchVisualInfo(wsDisplay, wsScreen, depth, TrueColor, &win->VisualInfo); /* --- */ win->AtomLeaderClient = XInternAtom(wsDisplay, "WM_CLIENT_LEADER", False); win->AtomDeleteWindow = XInternAtom(wsDisplay, "WM_DELETE_WINDOW", False); win->AtomTakeFocus = XInternAtom(wsDisplay, "WM_TAKE_FOCUS", False); win->AtomRolle = XInternAtom(wsDisplay, "WM_WINDOW_ROLE", False); win->AtomWMSizeHint = XInternAtom(wsDisplay, "WM_SIZE_HINT", False); win->AtomWMNormalHint = XInternAtom(wsDisplay, "WM_NORMAL_HINT", False); win->AtomProtocols = XInternAtom(wsDisplay, "WM_PROTOCOLS", False); win->AtomsProtocols[0] = win->AtomDeleteWindow; win->AtomsProtocols[1] = win->AtomTakeFocus; win->AtomsProtocols[2] = win->AtomRolle; /* --- */ win->WindowAttrib.background_pixel = BlackPixel(wsDisplay, wsScreen); win->WindowAttrib.border_pixel = WhitePixel(wsDisplay, wsScreen); win->WindowAttrib.colormap = XCreateColormap(wsDisplay, wsRootWin, win->VisualInfo.visual, AllocNone); win->WindowAttrib.event_mask = StructureNotifyMask | FocusChangeMask | ExposureMask | PropertyChangeMask | EnterWindowMask | LeaveWindowMask | VisibilityChangeMask | KeyPressMask | KeyReleaseMask; if ((cV & wsHandleMouseButton)) win->WindowAttrib.event_mask |= ButtonPressMask | ButtonReleaseMask; if ((cV & wsHandleMouseMove)) win->WindowAttrib.event_mask |= PointerMotionMask; win->WindowAttrib.cursor = win->wsCursor; win->WindowAttrib.override_redirect = False; if (D & wsOverredirect) win->WindowAttrib.override_redirect = True; win->WindowMask = CWBackPixel | CWBorderPixel | CWColormap | CWEventMask | CWCursor | CWOverrideRedirect; win->WindowID = XCreateWindow(wsDisplay, (win->Parent != 0 ? win->Parent : wsRootWin), win->X, win->Y, win->Width, win->Height, win->BorderWidth, win->VisualInfo.depth, InputOutput, win->VisualInfo.visual, win->WindowMask, &win->WindowAttrib); wsClassHint.res_name = "MPlayer"; wsClassHint.res_class = "MPlayer"; XSetClassHint(wsDisplay, win->WindowID, &wsClassHint); win->SizeHint.flags = PPosition | PSize | PResizeInc | PWinGravity; // | PBaseSize; win->SizeHint.x = win->X; win->SizeHint.y = win->Y; win->SizeHint.width = win->Width; win->SizeHint.height = win->Height; if (D & wsMinSize) { win->SizeHint.flags |= PMinSize; win->SizeHint.min_width = win->Width; win->SizeHint.min_height = win->Height; } if (D & wsMaxSize) { win->SizeHint.flags |= PMaxSize; win->SizeHint.max_width = win->Width; win->SizeHint.max_height = win->Height; } win->SizeHint.height_inc = 1; win->SizeHint.width_inc = 1; win->SizeHint.base_width = win->Width; win->SizeHint.base_height = win->Height; win->SizeHint.win_gravity = StaticGravity; XSetWMNormalHints(wsDisplay, win->WindowID, &win->SizeHint); win->WMHints.flags = InputHint | StateHint; win->WMHints.input = True; win->WMHints.initial_state = NormalState; XSetWMHints(wsDisplay, win->WindowID, &win->WMHints); wsWindowDecoration(win, win->Decorations); XStoreName(wsDisplay, win->WindowID, label); XmbSetWMProperties(wsDisplay, win->WindowID, label, label, NULL, 0, NULL, NULL, NULL); XSetWMProtocols(wsDisplay, win->WindowID, win->AtomsProtocols, 3); XChangeProperty(wsDisplay, win->WindowID, win->AtomLeaderClient, XA_WINDOW, 32, PropModeReplace, (unsigned char *)&LeaderWindow, 1); wsTextProperty.value = label; wsTextProperty.encoding = XA_STRING; wsTextProperty.format = 8; wsTextProperty.nitems = strlen(label); XSetWMIconName(wsDisplay, win->WindowID, &wsTextProperty); win->wGC = XCreateGC(wsDisplay, win->WindowID, GCForeground | GCBackground, &win->wGCV); win->Visible = wsNo; win->Focused = wsNo; win->Mapped = wsNo; win->Rolled = wsNo; if (D & wsShowWindow) XMapWindow(wsDisplay, win->WindowID); wsCreateImage(win, win->Width, win->Height); /* End of creating -------------------------------------------------------------------------- */ { int i; for (i = 0; i < wsWLCount; i++) if (wsWindowList[i] == NULL) break; if (i == wsWLCount) { mp_msg(MSGT_GPLAYER, MSGL_FATAL, MSGTR_WS_TooManyOpenWindows); mplayer(MPLAYER_EXIT_GUI, EXIT_ERROR, 0); } wsWindowList[i] = win; } XFlush(wsDisplay); XSync(wsDisplay, False); win->ReDraw = NULL; win->ReSize = NULL; win->Idle = NULL; win->MouseHandler = NULL; win->KeyHandler = NULL; mp_msg(MSGT_GPLAYER, MSGL_DBG2, "[ws] window is created. ( %s ).\n", label); } void wsDestroyWindow(wsTWindow *win) { int l; l = wsSearch(win->WindowID); if (l != -1) wsWindowList[l] = NULL; if (win->wsCursor != None) { XFreeCursor(wsDisplay, win->wsCursor); win->wsCursor = None; } XFreeGC(wsDisplay, win->wGC); XUnmapWindow(wsDisplay, win->WindowID); wsDestroyImage(win); XDestroyWindow(wsDisplay, win->WindowID); #if 0 win->ReDraw = NULL; win->ReSize = NULL; win->Idle = NULL; win->MouseHandler = NULL; win->KeyHandler = NULL; win->Visible = wsNo; win->Focused = wsNo; win->Mapped = wsNo; win->Rolled = wsNo; #endif } /** * @brief Handle automatic hiding of the cursor. */ void wsAutohideCursor(void) { if (mouse_win && (GetTimerMS() - mouse_time >= MOUSEHIDE_DELAY)) { wsVisibleMouse(mouse_win, wsHideMouseCursor); mouse_win = NULL; } } // ---------------------------------------------------------------------------------------------- // Handle events. // ---------------------------------------------------------------------------------------------- Bool wsEvents(Display *display, XEvent *Event) { unsigned long i = 0; int l; int x, y; Window child_window = 0; l = wsSearch(Event->xany.window); if (l == -1) return !wsTrue; wsWindowList[l]->State = wsNone; switch (Event->type) { case ClientMessage: if (Event->xclient.message_type == wsWindowList[l]->AtomProtocols) { if ((Atom)Event->xclient.data.l[0] == wsWindowList[l]->AtomDeleteWindow) { i = wsWindowClosed; goto expose; } if ((Atom)Event->xclient.data.l[0] == wsWindowList[l]->AtomTakeFocus) { i = wsWindowFocusIn; wsWindowList[l]->Focused = wsFocused; goto expose; } if ((Atom)Event->xclient.data.l[0] == wsWindowList[l]->AtomRolle) { mp_msg(MSGT_GPLAYER, MSGL_V, "[ws] role set.\n"); } } else { /* try to process DND events */ wsXDNDProcessClientMessage(&Event->xclient); } break; case MapNotify: i = wsWindowMapped; wsWindowList[l]->Mapped = wsMapped; goto expose; case UnmapNotify: i = wsWindowUnmapped; wsWindowList[l]->Mapped = wsNo; goto expose; case FocusIn: if (wsWindowList[l]->Focused == wsFocused) break; i = wsWindowFocusIn; wsWindowList[l]->Focused = wsFocused; goto expose; case FocusOut: if (wsWindowList[l]->Focused == wsNo) break; i = wsWindowFocusOut; wsWindowList[l]->Focused = wsNo; goto expose; case VisibilityNotify: switch (Event->xvisibility.state) { case VisibilityUnobscured: i = wsWindowVisible; wsWindowList[l]->Visible = wsVisible; goto expose; case VisibilityFullyObscured: i = wsWindowNotVisible; wsWindowList[l]->Visible = wsNotVisible; goto expose; case VisibilityPartiallyObscured: i = wsWindowPartialVisible; wsWindowList[l]->Visible = wsPVisible; goto expose; } expose: wsWindowList[l]->State = i; if (wsWindowList[l]->ReDraw) wsWindowList[l]->ReDraw(); break; case Expose: wsWindowList[l]->State = wsWindowExpose; if ((wsWindowList[l]->ReDraw) && (!Event->xexpose.count)) wsWindowList[l]->ReDraw(); break; case ConfigureNotify: XTranslateCoordinates(wsDisplay, wsWindowList[l]->WindowID, wsRootWin, 0, 0, &x, &y, &child_window); if ((wsWindowList[l]->X != x) || (wsWindowList[l]->Y != y) || (wsWindowList[l]->Width != Event->xconfigure.width) || (wsWindowList[l]->Height != Event->xconfigure.height)) { wsWindowList[l]->X = x; wsWindowList[l]->Y = y; wsWindowList[l]->Width = Event->xconfigure.width; wsWindowList[l]->Height = Event->xconfigure.height; if (wsWindowList[l]->ReSize) wsWindowList[l]->ReSize(wsWindowList[l]->X, wsWindowList[l]->Y, wsWindowList[l]->Width, wsWindowList[l]->Height); } wsWindowList[l]->Rolled = wsNo; if (Event->xconfigure.y < 0) { i = wsWindowRolled; wsWindowList[l]->Rolled = wsRolled; goto expose; } break; case KeyPress: i = wsKeyPressed; goto keypressed; case KeyRelease: i = wsKeyReleased; keypressed: wsWindowList[l]->Alt = False; wsWindowList[l]->Shift = False; wsWindowList[l]->NumLock = False; wsWindowList[l]->Control = False; wsWindowList[l]->CapsLock = False; if (Event->xkey.state & Mod1Mask) wsWindowList[l]->Alt = True; if (Event->xkey.state & Mod2Mask) wsWindowList[l]->NumLock = True; if (Event->xkey.state & ControlMask) wsWindowList[l]->Control = True; if (Event->xkey.state & ShiftMask) wsWindowList[l]->Shift = True; if (Event->xkey.state & LockMask) wsWindowList[l]->CapsLock = True; #if 0 { KeySym keySym; keySym = XKeycodeToKeysym(wsDisplay, Event->xkey.keycode, 0); if (keySym != NoSymbol) { keySym = ((keySym & 0xff00) != 0 ? ((keySym & 0x00ff) + 256) : (keySym)); wsKeyTable[keySym] = i; if (wsWindowList[l]->KeyHandler) wsWindowList[l]->KeyHandler(Event->xkey.state, i, keySym); } } #else { int key; char buf[100]; KeySym keySym; static XComposeStatus stat; XLookupString(&Event->xkey, buf, sizeof(buf), &keySym, &stat); key = ((keySym & 0xff00) != 0 ? ((keySym & 0x00ff) + 256) : (keySym)); wsKeyTable[key] = i; if (wsWindowList[l]->KeyHandler) wsWindowList[l]->KeyHandler(Event->xkey.keycode, i, key); } #endif break; case MotionNotify: i = wsMoveMouse; { /* pump all motion events from the display queue: * this way it works faster when moving the window */ static XEvent e; if (Event->xmotion.state) { while (XCheckTypedWindowEvent(display, Event->xany.window, MotionNotify, &e)) { /* FIXME: need to make sure we didn't release/press the button in between...*/ /* FIXME: do we need some timeout here to make sure we don't spend too much time * removing events from the queue? */ Event = &e; } } } if (wsWindowList[l]->wsCursor != None) { wsVisibleMouse(wsWindowList[l], wsShowMouseCursor); mouse_win = wsWindowList[l]; mouse_time = GetTimerMS(); } goto buttonreleased; case ButtonRelease: i = Event->xbutton.button + 128; if (wsWindowList[l]->wsCursor != None) { wsVisibleMouse(wsWindowList[l], wsShowMouseCursor); mouse_win = wsWindowList[l]; mouse_time = GetTimerMS(); } goto buttonreleased; case ButtonPress: i = Event->xbutton.button; if (wsWindowList[l]->wsCursor != None) { wsVisibleMouse(wsWindowList[l], wsShowMouseCursor); mouse_win = wsWindowList[l]; mouse_time = GetTimerMS(); } goto buttonreleased; case EnterNotify: i = wsEnterWindow; goto buttonreleased; case LeaveNotify: i = wsLeaveWindow; buttonreleased: if (wsWindowList[l]->MouseHandler) wsWindowList[l]->MouseHandler(i, Event->xbutton.x, Event->xbutton.y, Event->xmotion.x_root, Event->xmotion.y_root); break; case SelectionNotify: /* Handle DandD */ wsXDNDProcessSelection(wsWindowList[l], Event); break; } XFlush(wsDisplay); XSync(wsDisplay, False); return !wsTrue; } void wsHandleEvents(void) { /* handle pending events */ while (XPending(wsDisplay)) { XNextEvent(wsDisplay, &wsEvent); // printf("### X event: %d [%d]\n",wsEvent.type,delay); wsEvents(wsDisplay, &wsEvent); } } void wsMainLoop(void) { int delay = 20; mp_msg(MSGT_GPLAYER, MSGL_V, "[ws] init threads: %d\n", XInitThreads()); XSynchronize(wsDisplay, False); XLockDisplay(wsDisplay); // XIfEvent( wsDisplay,&wsEvent,wsEvents ); while (wsTrue) { /* handle pending events */ while (XPending(wsDisplay)) { XNextEvent(wsDisplay, &wsEvent); wsEvents(wsDisplay, &wsEvent); delay = 0; } usleep(delay * 1000); // FIXME! if (delay < 10 * 20) delay += 20; // pump up delay up to 0.2 sec (low activity) } XUnlockDisplay(wsDisplay); } // ---------------------------------------------------------------------------------------------- // Move window to selected layer // ---------------------------------------------------------------------------------------------- #define WIN_LAYER_ONBOTTOM 2 #define WIN_LAYER_NORMAL 4 #define WIN_LAYER_ONTOP 10 void wsSetLayer(Display *wsDisplay, Window win, int layer) { vo_x11_setlayer(wsDisplay, win, layer); } /** * @brief Switch window fullscreen state. * * Switch normal window to fullscreen and fullscreen window to normal. * * @param win pointer to a ws window structure */ void wsFullScreen(wsTWindow *win) { if (win->isFullScreen) { if (vo_fs_type & vo_wm_FULLSCREEN) /* window manager supports EWMH */ vo_x11_ewmh_fullscreen(win->WindowID, _NET_WM_STATE_REMOVE); else { win->X = win->OldX; win->Y = win->OldY; win->Width = win->OldWidth; win->Height = win->OldHeight; } win->isFullScreen = False; } else { if (vo_fs_type & vo_wm_FULLSCREEN) /* window manager supports EWMH */ vo_x11_ewmh_fullscreen(win->WindowID, _NET_WM_STATE_ADD); else { win->OldX = win->X; win->OldY = win->Y; win->OldWidth = win->Width; win->OldHeight = win->Height; } win->isFullScreen = True; wsUpdateXineramaInfo(win); } /* unknown window manager and obsolete option -fsmode used */ if (vo_wm_type == 0 && !(vo_fsmode & 16)) { XUnmapWindow(wsDisplay, win->WindowID); // required for MWM XWithdrawWindow(wsDisplay, win->WindowID, wsScreen); } /* restore window if window manager doesn't support EWMH */ if (!(vo_fs_type & vo_wm_FULLSCREEN)) { wsWindowDecoration(win, win->Decorations && !win->isFullScreen); vo_x11_sizehint(win->X, win->Y, win->Width, win->Height, 0); wsSetLayer(wsDisplay, win->WindowID, win->isFullScreen); XMoveResizeWindow(wsDisplay, win->WindowID, win->X, win->Y, win->Width, win->Height); } /* some window managers lose ontop after fullscreen */ if (!win->isFullScreen & vo_ontop) wsSetLayer(wsDisplay, win->WindowID, vo_ontop); wsRaiseWindowTop(wsDisplay, win->WindowID); XFlush(wsDisplay); } // ---------------------------------------------------------------------------------------------- // Redraw screen. // ---------------------------------------------------------------------------------------------- void wsPostRedisplay(wsTWindow *win) { if (win->ReDraw) { win->State = wsWindowExpose; win->ReDraw(); XFlush(wsDisplay); } } // ---------------------------------------------------------------------------------------------- // Do Exit. // ---------------------------------------------------------------------------------------------- void wsDoExit(void) { wsTrue = False; wsResizeWindow(wsWindowList[0], 32, 32); } // ---------------------------------------------------------------------------------------------- // Put 'Image' to window. // ---------------------------------------------------------------------------------------------- void wsConvert(wsTWindow *win, unsigned char *Image) { const uint8_t *src[4] = { Image, NULL, NULL, NULL }; int src_stride[4] = { 4 * win->xImage->width, 0, 0, 0 }; uint8_t *dst[4] = { win->ImageData, NULL, NULL, NULL }; int dst_stride[4]; int i; sws_ctx = sws_getCachedContext(sws_ctx, win->xImage->width, win->xImage->height, PIX_FMT_RGB32, win->xImage->width, win->xImage->height, out_pix_fmt, SWS_POINT, NULL, NULL, NULL); av_image_fill_linesizes(dst_stride, out_pix_fmt, win->xImage->width); sws_scale(sws_ctx, src, src_stride, 0, win->xImage->height, dst, dst_stride); if (!wsNonNativeOrder) return; switch (win->xImage->bits_per_pixel) { case 32: { uint32_t *d = (uint32_t *)win->ImageData; for (i = 0; i < win->xImage->width * win->xImage->height; i++) d[i] = bswap_32(d[i]); break; } case 16: case 15: { uint16_t *d = (uint16_t *)win->ImageData; for (i = 0; i < win->xImage->width * win->xImage->height; i++) d[i] = bswap_16(d[i]); break; } } } void wsPutImage(wsTWindow *win) { #ifdef HAVE_SHM if (wsUseXShm) { XShmPutImage(wsDisplay, win->WindowID, win->wGC, win->xImage, 0, 0, (win->Width - win->xImage->width) / 2, (win->Height - win->xImage->height) / 2, win->xImage->width, win->xImage->height, 0); } else #endif { XPutImage(wsDisplay, win->WindowID, win->wGC, win->xImage, 0, 0, (win->Width - win->xImage->width) / 2, (win->Height - win->xImage->height) / 2, win->xImage->width, win->xImage->height); } } // ---------------------------------------------------------------------------------------------- // Move window to x, y. // ---------------------------------------------------------------------------------------------- void wsMoveWindow(wsTWindow *win, Bool abs, int x, int y) { if (abs) { win->X = x; win->Y = y; } else wsWindowPosition(win, x, y, win->Width, win->Height); win->SizeHint.flags = PPosition | PWinGravity; win->SizeHint.x = win->X; win->SizeHint.y = win->Y; win->SizeHint.win_gravity = StaticGravity; XSetWMNormalHints(wsDisplay, win->WindowID, &win->SizeHint); XMoveWindow(wsDisplay, win->WindowID, win->X, win->Y); if (win->ReSize) win->ReSize(win->X, win->Y, win->Width, win->Height); } /** * @brief Move the window to the x and y position, but if it no longer fits * into the screen, reposition it towards the upper left. * * @param win pointer to a ws window structure * @param abs flag whether the position is real/absolute (True) or mock (False) * @param x x position of the window (real/absolute or mock) * @param y y position of the window (real/absolute or mock) */ void wsMoveWindowWithin(wsTWindow *win, Bool abs, int x, int y) { Bool fitting = True; wsMoveWindow(win, abs, x, y); if (win->X + win->Width + 1 > wsMaxX) { fitting = False; win->X = wsMaxX - win->Width; if (win->X < 0) win->X = 0; } if (win->Y + win->Height + 1 > wsMaxY) { fitting = False; win->Y = wsMaxY - win->Height; if (win->Y < 0) win->Y = 0; } if (!fitting) wsMoveWindow(win, True, win->X, win->Y); } // ---------------------------------------------------------------------------------------------- // Resize window to sx, sy. // ---------------------------------------------------------------------------------------------- void wsResizeWindow(wsTWindow *win, int sx, int sy) { win->Width = sx; win->Height = sy; win->SizeHint.flags = PPosition | PSize | PWinGravity; // | PBaseSize; win->SizeHint.x = win->X; win->SizeHint.y = win->Y; win->SizeHint.width = win->Width; win->SizeHint.height = win->Height; if (win->Property & wsMinSize) { win->SizeHint.flags |= PMinSize; win->SizeHint.min_width = win->Width; win->SizeHint.min_height = win->Height; } if (win->Property & wsMaxSize) { win->SizeHint.flags |= PMaxSize; win->SizeHint.max_width = win->Width; win->SizeHint.max_height = win->Height; } win->SizeHint.win_gravity = StaticGravity; win->SizeHint.base_width = sx; win->SizeHint.base_height = sy; if (vo_wm_type == 0) XUnmapWindow(wsDisplay, win->WindowID); XSetWMNormalHints(wsDisplay, win->WindowID, &win->SizeHint); XResizeWindow(wsDisplay, win->WindowID, sx, sy); if (win->ReSize) win->ReSize(win->X, win->Y, win->Width, win->Height); if (vo_wm_type == 0) XMapWindow(wsDisplay, win->WindowID); } /** * @brief Iconify a window. * * @param win pointer to a ws window structure */ void wsIconify(wsTWindow *win) { XIconifyWindow(wsDisplay, win->WindowID, 0); } /** * @brief Map a window and raise it to the top. * * @param dpy display * @param win window */ void wsRaiseWindowTop(Display *dpy, Window win) { XMapRaised(dpy, win); XRaiseWindow(dpy, win); } // ---------------------------------------------------------------------------------------------- // Set window background to 'color'. // ---------------------------------------------------------------------------------------------- void wsSetBackground(wsTWindow *win, int color) { XSetWindowBackground(wsDisplay, win->WindowID, color); } void wsSetBackgroundRGB(wsTWindow *win, int r, int g, int b) { int color = 0; switch (wsOutMask) { case wsRGB32: case wsRGB24: color = (r << 16) + (g << 8) + b; break; case wsBGR32: case wsBGR24: color = (b << 16) + (g << 8) + r; break; case wsRGB16: PACK_RGB16(b, g, r, color); break; case wsBGR16: PACK_RGB16(r, g, b, color); break; case wsRGB15: PACK_RGB15(b, g, r, color); break; case wsBGR15: PACK_RGB15(r, g, b, color); break; } XSetWindowBackground(wsDisplay, win->WindowID, color); } void wsSetForegroundRGB(wsTWindow *win, int r, int g, int b) { int color = 0; switch (wsOutMask) { case wsRGB32: case wsRGB24: color = (r << 16) + (g << 8) + b; break; case wsBGR32: case wsBGR24: color = (b << 16) + (g << 8) + r; break; case wsRGB16: PACK_RGB16(b, g, r, color); break; case wsBGR16: PACK_RGB16(r, g, b, color); break; case wsRGB15: PACK_RGB15(b, g, r, color); break; case wsBGR15: PACK_RGB15(r, g, b, color); break; } XSetForeground(wsDisplay, win->wGC, color); } // ---------------------------------------------------------------------------------------------- // Show / hide mouse cursor. // ---------------------------------------------------------------------------------------------- void wsVisibleMouse(wsTWindow *win, int m) { switch (m) { case wsShowMouseCursor: if (win->wsCursor != None) { XFreeCursor(wsDisplay, win->wsCursor); win->wsCursor = None; } XDefineCursor(wsDisplay, win->WindowID, 0); break; case wsHideMouseCursor: win->wsCursor = XCreatePixmapCursor(wsDisplay, win->wsCursorPixmap, win->wsCursorPixmap, &win->wsColor, &win->wsColor, 0, 0); XDefineCursor(wsDisplay, win->WindowID, win->wsCursor); break; } XFlush(wsDisplay); } int wsGetDepthOnScreen(void) { int depth; XImage *mXImage; Visual *visual; if ((depth = vo_find_depth_from_visuals(wsDisplay, wsScreen, &visual)) > 0) { mXImage = XCreateImage(wsDisplay, visual, depth, ZPixmap, 0, NULL, 1, 1, 32, 0); wsDepthOnScreen = mXImage->bits_per_pixel; wsRedMask = mXImage->red_mask; wsGreenMask = mXImage->green_mask; wsBlueMask = mXImage->blue_mask; #if HAVE_BIGENDIAN wsNonNativeOrder = mXImage->byte_order == LSBFirst; #else wsNonNativeOrder = mXImage->byte_order == MSBFirst; #endif XDestroyImage(mXImage); } else { int bpp, ibpp; XWindowAttributes attribs; mXImage = XGetImage(wsDisplay, wsRootWin, 0, 0, 1, 1, AllPlanes, ZPixmap); bpp = mXImage->bits_per_pixel; XGetWindowAttributes(wsDisplay, wsRootWin, &attribs); ibpp = attribs.depth; mXImage = XGetImage(wsDisplay, wsRootWin, 0, 0, 1, 1, AllPlanes, ZPixmap); bpp = mXImage->bits_per_pixel; if ((ibpp + 7) / 8 != (bpp + 7) / 8) ibpp = bpp; wsDepthOnScreen = ibpp; wsRedMask = mXImage->red_mask; wsGreenMask = mXImage->green_mask; wsBlueMask = mXImage->blue_mask; XDestroyImage(mXImage); } return wsDepthOnScreen; } void wsXDone(void) { XCloseDisplay(wsDisplay); } void wsVisibleWindow(wsTWindow *win, int show) { switch (show) { case wsShowWindow: XMapRaised(wsDisplay, win->WindowID); if (vo_fs_type & vo_wm_FULLSCREEN) win->isFullScreen = False; break; case wsHideWindow: XUnmapWindow(wsDisplay, win->WindowID); break; } XFlush(wsDisplay); } void wsDestroyImage(wsTWindow *win) { if (win->xImage) { XDestroyImage(win->xImage); #ifdef HAVE_SHM if (wsUseXShm) { XShmDetach(wsDisplay, &win->Shminfo); shmdt(win->Shminfo.shmaddr); } #endif } win->xImage = NULL; } void wsCreateImage(wsTWindow *win, int Width, int Height) { #ifdef HAVE_SHM if (wsUseXShm) { win->xImage = XShmCreateImage(wsDisplay, win->VisualInfo.visual, win->VisualInfo.depth, ZPixmap, NULL, &win->Shminfo, Width, Height); if (win->xImage == NULL) { mp_msg(MSGT_GPLAYER, MSGL_FATAL, MSGTR_WS_ShmError); mplayer(MPLAYER_EXIT_GUI, EXIT_ERROR, 0); } win->Shminfo.shmid = shmget(IPC_PRIVATE, win->xImage->bytes_per_line * win->xImage->height, IPC_CREAT | 0777); if (win->Shminfo.shmid < 0) { XDestroyImage(win->xImage); mp_msg(MSGT_GPLAYER, MSGL_FATAL, MSGTR_WS_ShmError); mplayer(MPLAYER_EXIT_GUI, EXIT_ERROR, 0); } win->Shminfo.shmaddr = (char *)shmat(win->Shminfo.shmid, 0, 0); if (win->Shminfo.shmaddr == ((char *)-1)) { XDestroyImage(win->xImage); if (win->Shminfo.shmaddr != ((char *)-1)) shmdt(win->Shminfo.shmaddr); mp_msg(MSGT_GPLAYER, MSGL_FATAL, MSGTR_WS_ShmError); mplayer(MPLAYER_EXIT_GUI, EXIT_ERROR, 0); } win->xImage->data = win->Shminfo.shmaddr; win->Shminfo.readOnly = False; XShmAttach(wsDisplay, &win->Shminfo); XSync(wsDisplay, False); shmctl(win->Shminfo.shmid, IPC_RMID, 0); } else #endif { win->xImage = XCreateImage(wsDisplay, win->VisualInfo.visual, win->VisualInfo.depth, ZPixmap, 0, 0, Width, Height, (wsDepthOnScreen == 3) ? 32 : wsDepthOnScreen, 0); if ((win->xImage->data = malloc(win->xImage->bytes_per_line * win->xImage->height)) == NULL) { mp_msg(MSGT_GPLAYER, MSGL_FATAL, MSGTR_WS_NotEnoughMemoryDrawBuffer); mplayer(MPLAYER_EXIT_GUI, EXIT_ERROR, 0); } } win->ImageData = (unsigned char *)win->xImage->data; win->ImageDataw = (unsigned short int *)win->xImage->data; win->ImageDatadw = (unsigned int *)win->xImage->data; } void wsResizeImage(wsTWindow *win, int Width, int Height) { wsDestroyImage(win); wsCreateImage(win, Width, Height); } int wsGetOutMask(void) { if ((wsDepthOnScreen == 32) && (wsRedMask == 0xff0000) && (wsGreenMask == 0x00ff00) && (wsBlueMask == 0x0000ff)) return wsRGB32; if ((wsDepthOnScreen == 32) && (wsRedMask == 0x0000ff) && (wsGreenMask == 0x00ff00) && (wsBlueMask == 0xff0000)) return wsBGR32; if ((wsDepthOnScreen == 24) && (wsRedMask == 0xff0000) && (wsGreenMask == 0x00ff00) && (wsBlueMask == 0x0000ff)) return wsRGB24; if ((wsDepthOnScreen == 24) && (wsRedMask == 0x0000ff) && (wsGreenMask == 0x00ff00) && (wsBlueMask == 0xff0000)) return wsBGR24; if ((wsDepthOnScreen == 16) && (wsRedMask == 0xf800) && (wsGreenMask == 0x7e0) && (wsBlueMask == 0x1f)) return wsRGB16; if ((wsDepthOnScreen == 16) && (wsRedMask == 0x1f) && (wsGreenMask == 0x7e0) && (wsBlueMask == 0xf800)) return wsBGR16; if ((wsDepthOnScreen == 15) && (wsRedMask == 0x7c00) && (wsGreenMask == 0x3e0) && (wsBlueMask == 0x1f)) return wsRGB15; if ((wsDepthOnScreen == 15) && (wsRedMask == 0x1f) && (wsGreenMask == 0x3e0) && (wsBlueMask == 0x7c00)) return wsBGR15; return 0; } void wsSetTitle(wsTWindow *win, char *name) { XStoreName(wsDisplay, win->WindowID, name); } void wsSetMousePosition(wsTWindow *win, int x, int y) { XWarpPointer(wsDisplay, wsRootWin, win->WindowID, 0, 0, 0, 0, x, y); } void wsSetShape(wsTWindow *win, char *data) { #ifdef CONFIG_XSHAPE if (!wsUseXShape) return; if (data) { win->Mask = XCreateBitmapFromData(wsDisplay, win->WindowID, data, win->Width, win->Height); XShapeCombineMask(wsDisplay, win->WindowID, ShapeBounding, 0, 0, win->Mask, ShapeSet); XFreePixmap(wsDisplay, win->Mask); } else XShapeCombineMask(wsDisplay, win->WindowID, ShapeBounding, 0, 0, None, ShapeSet); #endif } /** * @brief Set differently sized icons to a window. * * This function sets the X icon hint as well as * the properties KWM_WIN_ICON and _NET_WM_ICON. * * @param dpy display * @param win window * @param icon pointer to the icons */ void wsSetIcon(Display *dpy, Window win, guiIcon_t *icon) { XWMHints *wm; Atom iconatom; long data[2]; if (icon->normal) { wm = XGetWMHints(dpy, win); if (!wm) wm = XAllocWMHints(); wm->icon_pixmap = icon->normal; wm->icon_mask = icon->normal_mask; wm->flags |= IconPixmapHint | IconMaskHint; XSetWMHints(dpy, win, wm); XFree(wm); } if (icon->small || icon->normal) { iconatom = XInternAtom(dpy, "KWM_WIN_ICON", False); data[0] = (icon->small ? icon->small : icon->normal); data[1] = (icon->small ? icon->small_mask : icon->normal_mask); XChangeProperty(dpy, win, iconatom, iconatom, 32, PropModeReplace, (unsigned char *)data, 2); } if (icon->collection) { iconatom = XInternAtom(dpy, "_NET_WM_ICON", False); XChangeProperty(dpy, win, iconatom, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)icon->collection, icon->collection_size); } }