Files
libsst/libsst-wm/Xlib/SST_WMWindow_Xlib.c
2026-04-03 00:22:39 -05:00

538 lines
14 KiB
C

/*
SST_WMWindow_Xlib.c
Author: Patrick Baggett <ptbaggett@762studios.com>
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 <SST/SST_WMWindow.h>
#include "XlibPrivate.h"
#include "XI2Private.h"
#include "EventQueue.h"
#include "APIPrivate.h"
#include <stdio.h>
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; i<nrItems; i++)
{
if(propReturn[i] == actionFullscreen)
{
foundit = 1;
break;
}
}
/* Free atom list and return */
X.Free(propReturn);
return foundit;
}
/******************************************************************************/
/* Use extended window manager hints to set the window in/out of fullscreen mode */
static void ewmh_setFullscreen(SST_Window_Xlib* win, int fullscreen)
{
XEvent event;
Display* display = win->owner->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
};