/* SST_WMWindow_Xlib.c Author: Patrick Baggett Created: 1/8/2013 Purpose: Window creation (Xlib) License: This program is free software. It comes without any warranty, to the extent permitted by applicable law. You can redistribute it and/or modify it under the terms of the Do What The Fuck You Want To Public License, Version 2, as published by Sam Hocevar. See http://sam.zoy.org/wtfpl/COPYING for more details. */ #include #include "XlibPrivate.h" #include "XI2Private.h" #include "EventQueue.h" #include "APIPrivate.h" #include static void destroyWin(SST_Window_Xlib* win); /* Extended Window Manager Hints (http://standards.freedesktop.org/wm-spec/wm-spec-latest.html) */ static int ewmh_supported(SST_DisplayTarget_Xlib* displayTarget, SST_Window_Xlib* win); static void ewmh_setFullscreen(SST_Window_Xlib* win, int fullscreen); /******************************************************************************/ static SST_DisplayTarget Xlib_CreateDisplayTarget(size_t adapterIndex, size_t screenIndexOrMultihead) { SST_DisplayTarget_Xlib* displayTarget; #ifdef HAVE_XINPUT2 XI2Functions XI; #endif Display* display; /* > 1 adapters not supported, multihead not supported */ if(screenIndexOrMultihead != 0 || adapterIndex != 0) return NULL; /* Open the X display */ display = X.OpenDisplay(NULL); if(display == NULL) return NULL; /* If we can open a display, then we're connected to the X server, so that is good enough. */ /* Allocate a display target structure */ displayTarget = (SST_DisplayTarget_Xlib*)malloc(sizeof(SST_DisplayTarget_Xlib)); if(displayTarget == NULL) { X.CloseDisplay(display); return NULL; } /* Initialize lock for user events */ if(pthread_mutex_init(&displayTarget->eventLock, NULL) != 0) { X.CloseDisplay(display); free(displayTarget); return NULL; } if(InitEQ(&displayTarget->eventQueue) == 0) { pthread_mutex_destroy(&displayTarget->eventLock); X.CloseDisplay(display); free(displayTarget); return NULL; } if(InitEQ(&displayTarget->userEventQueue) == 0) { DestroyEQ(&displayTarget->eventQueue); pthread_mutex_destroy(&displayTarget->eventLock); X.CloseDisplay(display); free(displayTarget); return NULL; } /* Save fields */ displayTarget->display = display; displayTarget->firstWindow = NULL; displayTarget->relativeMouse = 0; displayTarget->ewmhSupport = -1; displayTarget->atomWmProtocols = X.InternAtom(display, "WM_PROTOCOLS", True); displayTarget->atomWmDeleteWindow = X.InternAtom(display, "WM_DELETE_WINDOW", False); memset(displayTarget->keymapBitvector, 0, sizeof(displayTarget->keymapBitvector)); /* XInput2: Verify that server supports XI2 (sets */ #ifdef HAVE_XINPUT2 if(XI2_IsLoaded()) XI2_IsSupportedOnConnection(displayTarget); #endif return (SST_DisplayTarget)displayTarget; } /******************************************************************************/ static size_t Xlib_GetDisplayTargetScreenCount(SST_DisplayTarget target) { (void)target; return 1; } /******************************************************************************/ static SST_Window Xlib_CreateWindowOnScreen(SST_DisplayTarget target, size_t screenIndex, uint32_t x, uint32_t y, uint32_t width, uint32_t height, const char* title) { SST_Window_Xlib* win; SST_DisplayTarget_Xlib* displayTarget = (SST_DisplayTarget_Xlib*)target; Window xwin; Display* display = displayTarget->display; int screen = DefaultScreen(display); SST_WMEvent* event; /* Must be on screen 0 */ if(screenIndex != 0) return 0; xwin = X.CreateSimpleWindow(display, RootWindow(display, screen), (int)x, (int)y, (int)width, (int)height, 0, 0, /* border width/color */ WhitePixel(display, screen)); /* background color */ /* Set window's WM_PROTOCOLS property */ X.SetWMProtocols(display, xwin, &displayTarget->atomWmDeleteWindow, 1); /* Set title bar text */ X.StoreName(display, xwin, title); /* Map the window to the screen */ X.MapRaised(display, xwin); /* Force the move, because some window managers just put windows wherever. Argh. */ X.MoveWindow(display, xwin, (int)x, (int)y); X.SelectInput(display, xwin, KeyPressMask| KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask | ExposureMask | StructureNotifyMask); /* Display it now */ X.Flush(display); /* Allocate SST window structure */ win = (SST_Window_Xlib*)malloc(sizeof(SST_Window_Xlib)); if(win == NULL) return NULL; win->owner = displayTarget; win->next = displayTarget->firstWindow; win->xwin = xwin; win->lastX = (uint32_t)x; win->lastY = (uint32_t)y; win->lastWidth = (uint32_t)width; win->lastHeight = (uint32_t)height; win->isFullscreen = 0; win->softwareBackbuffer = NULL; win->softwareImage = NULL; win->softwarePitch = 0; /* Link window as new root */ displayTarget->firstWindow = win; /* Add a SSTWMEVENT_CREATED event */ event = AllocSlotInEQ(&displayTarget->eventQueue); if(event != NULL) { event->window = win; event->type = SSTWMEVENT_CREATED; memset(&event->details, 0, sizeof(event->details)); } return (SST_Window)win; } /******************************************************************************/ static SST_DisplayTarget Xlib_GetWindowDisplayTarget(SST_Window window) { SST_Window_Xlib* win = (SST_Window_Xlib*)window; return win->owner; } /******************************************************************************/ static void Xlib_SetWindowText(SST_Window window, const char* titleBar) { SST_Window_Xlib* win = (SST_Window_Xlib*)window; X.StoreName(win->owner->display, win->xwin, titleBar); } /******************************************************************************/ static void Xlib_GetWindowRect(SST_Window window, SST_Rect* rectReturn) { SST_Window_Xlib* win = (SST_Window_Xlib*)window; Window dummyRoot; int x, y; unsigned int w, h; unsigned int dummy1, dummy2; X.GetGeometry(win->owner->display, win->xwin, &dummyRoot, &x, &y, &w, &h, &dummy1, &dummy2); rectReturn->x = (uint32_t)x; rectReturn->y = (uint32_t)y; rectReturn->width = (uint32_t)w; rectReturn->height = (uint32_t)h; return; } /******************************************************************************/ static void Xlib_MoveWindowOnScreen(SST_Window window, size_t screenIndex, uint32_t x, uint32_t y) { SST_Window_Xlib* win = (SST_Window_Xlib*)window; /* Only allow operations on screen 0 */ if(screenIndex != SST_SAME_SCREEN && screenIndex != 0) return; X.MoveWindow(win->owner->display, win->xwin, (int)x, (int)y); X.Sync(win->owner->display, False); } /******************************************************************************/ static void Xlib_ResizeWindow(SST_Window window, uint32_t width, uint32_t height) { SST_Window_Xlib* win = (SST_Window_Xlib*)window; X.ResizeWindow(win->owner->display, win->xwin, width, height); X.Sync(win->owner->display, False); } /******************************************************************************/ static void Xlib_SetWindowState(SST_Window window, SST_WMWindowState state, uint32_t param) { SST_Window_Xlib* win = (SST_Window_Xlib*)window; Display* display = win->owner->display; switch(state) { case SSTWS_SHOWN: { /* Map or unmap the window respectively */ if(param == 0) X.UnmapWindow(display, win->xwin); else X.MapWindow(display, win->xwin); break; } /* Turn on/off resizeability */ case SSTWS_RESIZEABLE: { /* TODO */ break; } case SSTWS_FULLSCREEN: { /* If we haven't yet checked for EWMH support, do so now */ if(win->owner->ewmhSupport == -1) win->owner->ewmhSupport = ewmh_supported(win->owner, win); /* Enabling fullscreen? */ if(param && !win->isFullscreen) { /* Use EWMH */ if(win->owner->ewmhSupport != 0) ewmh_setFullscreen(win, 1); win->isFullscreen = True; } else if(win->isFullscreen) /* Disabling fullscreen */ { win->isFullscreen = False; if(win->owner->ewmhSupport != 0) ewmh_setFullscreen(win, 0); } break; } case SSTWS_MINIMIZED: { if(param == 0) /* Maximize */ X.MapRaised(display, win->xwin); else /* Minimize */ X.IconifyWindow(display, win->xwin, DefaultScreen(display)); break; } default: break; } } /******************************************************************************/ static void Xlib_SetDisplayTargetState(SST_DisplayTarget target, SST_WMDisplayTargetState state, uint32_t param) { SST_DisplayTarget_Xlib* displayTarget = (SST_DisplayTarget_Xlib*)target; switch(state) { case SSTDTS_RELMOUSE: { #ifdef HAVE_XINPUT2 XIEventMask eventmask; uint8_t maskBytes[3]; memset(maskBytes, 0, sizeof(maskBytes)); eventmask.deviceid = XIAllMasterDevices; eventmask.mask_len = sizeof(maskBytes); eventmask.mask = maskBytes; if(!displayTarget->xi2Support) { printf("libsst-wm: XInput2 not supported; cannot use relative mouse mode.\n"); return; } /* Disabling? */ if(displayTarget->relativeMouse && param == 0) { displayTarget->relativeMouse = 0; } else if(!displayTarget->relativeMouse && param != 0) /* Enabling? */ { printf("libsst-wm: enabling XI2 raw motion\n"); XISetMask(maskBytes, XI_RawMotion); /* Macro invocation */ displayTarget->relativeMouse = 1; } if(XI.SelectEvents(displayTarget->display, DefaultRootWindow(displayTarget->display), &eventmask, 1) == Success) printf("libsst-wm: relative mouse is %s!\n", param?"ON":"OFF"); else printf("libsst-wm: failed to change relative mouse mode - XISelectEvents() returned failure\n"); #else printf("libsst-wm: XI2 not compiled in, cannot use relative mouse mode."); #endif break; } } } /******************************************************************************/ static void Xlib_DestroyWindow(SST_Window window) { SST_DisplayTarget_Xlib* displayTarget; SST_Window_Xlib* win = (SST_Window_Xlib*)window; SST_Window_Xlib* nextWin; displayTarget = win->owner; nextWin = displayTarget->firstWindow; /* Special case: root window */ if(nextWin == win) { /* Set new root to be this->next */ displayTarget->firstWindow = win->next; } else { int found = 0; /* Check list */ while(nextWin) { /* Did we find the window? */ if(nextWin->next == win) { /* Remove this window from the linked list */ nextWin->next = win->next; found = 1; break; } else nextWin = nextWin->next; } /* Don't destroy another display target's window */ if(!found) return; } /* Actually destroy the window window */ destroyWin(win); } /******************************************************************************/ static void Xlib_DestroyDisplayTarget(SST_DisplayTarget target) { SST_DisplayTarget_Xlib* displayTarget = (SST_DisplayTarget_Xlib*)target; SST_Window_Xlib* window = displayTarget->firstWindow; /* Destroy all windows */ while(window) { /* Save the next window */ SST_Window_Xlib* next = window->next; destroyWin(window); /* Move to next window */ window = next; } DestroyEQ(&displayTarget->userEventQueue); DestroyEQ(&displayTarget->eventQueue); pthread_mutex_destroy(&displayTarget->eventLock); X.CloseDisplay(displayTarget->display); /* Free structures */ free(displayTarget); } /******************************************************************************/ static void destroyWin(SST_Window_Xlib* win) { /* TODO Destroy more */ X.DestroyWindow(win->owner->display, win->xwin); free(win); } /******************************************************************************/ /* Check if extended window manager hints are supported */ static int ewmh_supported(SST_DisplayTarget_Xlib* displayTarget, SST_Window_Xlib* win) { Atom actions; Atom actionFullscreen; Atom actualType; int format; unsigned long nrItems; unsigned long bytesAfterReturn; unsigned long i; Atom* propReturn; int foundit = 0; Display* display = displayTarget->display; actions = X.InternAtom(display, "_NET_WM_ALLOWED_ACTIONS", False); actionFullscreen = X.InternAtom(display, "_NET_WM_ACTION_FULLSCREEN", False); /* Get a list of atoms */ if(X.GetWindowProperty(display, win->xwin, actions, 0, 1024, False, XA_ATOM, &actualType, &format, &nrItems, &bytesAfterReturn, (unsigned char**)&propReturn) != Success) { /* Nope */ return 0; } /* Search through the list for the atom that matches the fullscreen atom */ for(i=0; iowner->display; event.xclient.type = ClientMessage; event.xclient.serial = 0; event.xclient.send_event = True; event.xclient.display = display; event.xclient.window = win->xwin; event.xclient.message_type = X.InternAtom(display, "_NET_WM_STATE", False); event.xclient.format = 32; event.xclient.data.l[0] = fullscreen; event.xclient.data.l[1] = X.InternAtom(display, "_NET_WM_STATE_FULLSCREEN", False); event.xclient.data.l[2] = 0; event.xclient.data.l[3] = 0; event.xclient.data.l[4] = 0; X.SendEvent(display, DefaultRootWindow(display), False, StructureNotifyMask | ResizeRedirectMask, &event); } extern int Xlib_ShowDialogBox(SST_DisplayTarget target, SST_Window parent, const char* caption, const char* message, const char** buttons, int nrButtons); const struct SST_WM_WindowFuncs Xlib_WindowFuncs = { Xlib_CreateDisplayTarget, Xlib_GetDisplayTargetScreenCount, Xlib_CreateWindowOnScreen, Xlib_GetWindowDisplayTarget, Xlib_SetWindowText, Xlib_GetWindowRect, Xlib_MoveWindowOnScreen, Xlib_ResizeWindow, Xlib_ShowDialogBox, Xlib_SetWindowState, Xlib_SetDisplayTargetState, Xlib_DestroyWindow, Xlib_DestroyDisplayTarget };