X-Git-Url: https://ecere.com/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=ecere%2Fsrc%2Fgui%2Fdrivers%2FXInterface.ec;h=c392eb1ddfc86b30ea2d60922c71e665ee3f19de;hb=adde9868695cab1932036e12b439660d8d935aea;hp=1fbd0e2ae427e50d992216959cf8167448d30578;hpb=8ee80ec2b5c2317d48ff1ac4b3a886a5d1bed7aa;p=sdk diff --git a/ecere/src/gui/drivers/XInterface.ec b/ecere/src/gui/drivers/XInterface.ec index 1fbd0e2..c392eb1 100644 --- a/ecere/src/gui/drivers/XInterface.ec +++ b/ecere/src/gui/drivers/XInterface.ec @@ -33,6 +33,10 @@ default: #define Time X11Time #define KeyCode X11KeyCode #define Picture X11Picture +#define Bool X11Bool + +#define _XTYPEDEF_BOOL +typedef int X11Bool; #include #include @@ -40,6 +44,7 @@ default: #include #include #include +#include #include #if !defined(ECERE_NO3D) && !defined(ECERE_NOGL) #include @@ -47,6 +52,7 @@ default: #include #include +#undef Bool #undef Picture #undef Window #undef Cursor @@ -92,6 +98,9 @@ static bool gotAnXEvent = false; static XEvent xEvent; static int joystickFD[4]; static X11Window activeWindow; +static X11Cursor systemCursors[SystemCursor]; + +static enum NETWMStateAction { remove = 0, add = 1, toggle = 2 }; static enum AtomIdents { @@ -102,10 +111,12 @@ static enum AtomIdents _net_wm_window_type_desktop, _net_wm_window_type_dialog, _net_wm_window_type_dock, _net_wm_window_type_dropdown_menu, _net_wm_window_type_menu, _net_wm_window_type_normal, _net_wm_window_type_popup_menu, _net_wm_window_type_splash, _net_wm_window_type_toolbar, _net_wm_window_type_utility, _net_workarea, _net_frame_extents, _net_request_frame_extents, - _net_wm_state_maximized_vert, _net_wm_state_maximized_horz, _net_wm_state_modal, app_selection + _net_wm_state_maximized_vert, _net_wm_state_maximized_horz, _net_wm_state_modal, app_selection, _net_supported, + _net_wm_state_skip_taskbar, _net_wm_state_fullscreen, _net_wm_state_above }; static Atom atoms[AtomIdents]; +static bool atomsSupported[AtomIdents]; static const char *atomNames[AtomIdents] = { "CLIPBOARD", //clipboard @@ -147,20 +158,17 @@ static const char *atomNames[AtomIdents] = { "_NET_WM_STATE_MAXIMIZED_VERT", // _net_wm_state_maximized_vert "_NET_WM_STATE_MAXIMIZED_HORZ", // _net_wm_state_maximized_horz "_NET_WM_STATE_MODAL", // _net_wm_state_modal - "APP_SELECTION" + "APP_SELECTION", + "_NET_SUPPORTED", + "_NET_WM_STATE_SKIP_TASKBAR", + "_NET_WM_STATE_FULLSCREEN", + "_NET_WM_STATE_ABOVE" }; /* _NET_WM_STATE_STICKY, ATOM -_NET_WM_STATE_MAXIMIZED_VERT, ATOM -_NET_WM_STATE_MAXIMIZED_HORZ, ATOM _NET_WM_STATE_SHADED, ATOM -_NET_WM_STATE_SKIP_TASKBAR, ATOM _NET_WM_STATE_SKIP_PAGER, ATOM -_NET_WM_STATE_HIDDEN, ATOM -_NET_WM_STATE_FULLSCREEN, ATOM -_NET_WM_STATE_ABOVE, ATOM _NET_WM_STATE_BELOW, ATOM -_NET_WM_STATE_DEMANDS_ATTENTION, ATOM */ static bool autoRepeatDetectable; @@ -170,7 +178,71 @@ PixelFormat xSystemPixelFormat; Visual * xSystemVisual; bool xSharedMemory; -static Time timeStamp; +static void SetNETWMState(X11Window windowHandle, bool throughRoot, NETWMStateAction action, Atom atom1, Atom atom2) +{ + if(atomsSupported[_net_wm_state]) + { + int format; + unsigned long count, fill; + Atom type; + char * data = null; + uint state = WithdrawnState; + + /*if(XGetWindowProperty(xGlobalDisplay, windowHandle, atoms[wm_state], 0, 3, False, + atoms[wm_state], &type, &format, &count, &fill, &data) == Success && count) + { + state = *(uint *)data; + XFree(data); + } */ + if(!throughRoot) //state == WithdrawnState) + { + // We need to handle modifying these ourselves for withdrawn windows... + if(action == add) + { + Atom values[2] = { atom1, atom2 }; + XChangeProperty(xGlobalDisplay, windowHandle, atoms[_net_wm_state], XA_ATOM, + 32, PropModeAppend, (byte *)values, atom2 ? 2 : 1); + } + else if(XGetWindowProperty(xGlobalDisplay, windowHandle, atoms[_net_wm_state], 0, 32, False, + XA_ATOM, &type, &format, &count, &fill, &data) == Success) + { + Atom * values = (Atom *) data; + int i; + for (i = 0; i < count; i++) + { + if(values[i] == atom1 || (atom2 && values[i] == atom2)) + { + if(i < count - 1) + memmove(values + i, values + i + 1, sizeof(Atom) * (count - i - 1)); + count--; + i--; + } + } + XChangeProperty(xGlobalDisplay, windowHandle, atoms[_net_wm_state], XA_ATOM, 32, PropModeReplace, (byte *)values, (int)count); + XFree(data); + } + } + else + { + XClientMessageEvent event = { 0 }; + event.type = ClientMessage; + event.message_type = atoms[_net_wm_state]; + event.display = xGlobalDisplay; + event.serial = 0; + event.window = windowHandle; + event.send_event = 1; + event.format = 32; + event.data.l[0] = action; + event.data.l[1] = atom1; + event.data.l[2] = atom2; + XSendEvent(xGlobalDisplay, DefaultRootWindow(xGlobalDisplay), bool::false, + SubstructureRedirectMask | SubstructureNotifyMask, (union _XEvent *)&event); + } + } + +} + +static X11Time timeStamp; class XWindowData : struct { @@ -181,6 +253,7 @@ public: // Decorations Size Box decor; bool gotFrameExtents; + bool currentlyVisible; }; bool XGetBorderWidths(Window window, Box box) @@ -248,7 +321,7 @@ static void RepositionDesktop(bool updateChildren) h = XDisplayHeight(xGlobalDisplay, DefaultScreen(xGlobalDisplay)); x_root = XRootWindowOfScreen(x_screen); - if(atoms[_net_number_of_desktops] != None) + if(atomsSupported[_net_number_of_desktops]) { if(XGetWindowProperty(xGlobalDisplay, x_root, atoms[_net_number_of_desktops], 0, 1, False, XA_CARDINAL, &type, &format, &len, &fill, @@ -269,7 +342,7 @@ static void RepositionDesktop(bool updateChildren) } } - if(atoms[_net_current_desktop] != None) + if(atomsSupported[_net_current_desktop]) { if(XGetWindowProperty(xGlobalDisplay, x_root, atoms[_net_current_desktop], 0, 1, False, XA_CARDINAL, &type, &format, &len, &fill, @@ -287,7 +360,7 @@ static void RepositionDesktop(bool updateChildren) //printf("_NET_CURRENT_DESKTOP is %d\n", current); } } - if(atoms[_net_workarea] != None) + if(atomsSupported[_net_workarea]) { long *workareas; @@ -327,11 +400,39 @@ static void RepositionDesktop(bool updateChildren) if(desktopX != x || desktopY != y || desktopW != w || desktopH != h) { - guiApp.SetDesktopPosition(x, y, w, h, updateChildren); - desktopX = x; - desktopY = y; - desktopW = w; - desktopH = h; + bool skip = false; + // Don't change the desktop area if another fullscreen application is running + // (Attemps to solve the debugging IDE being re-activated while switching debugged app between fullscreen/windowed on Cinnamon) + if(XGetWindowProperty(xGlobalDisplay, x_root, atoms[_net_active_window], 0, 32, + False, AnyPropertyType, &type, &format, &len, + &fill, &data) == Success && data) + { + X11Window active = *(long *)data; + XFree(data); + if(XGetWindowProperty(xGlobalDisplay, active, atoms[_net_wm_state], 0, 32, False, + XA_ATOM, &type, &format, &len, &fill, &data) == Success) + { + Atom * values = (Atom *) data; + int i; + for (i = 0; i < len; i++) + { + if(values[i] == atoms[_net_wm_state_fullscreen]) + { + skip = true; + break; + } + } + XFree(data); + } + } + if(w && h && !skip) + { + guiApp.SetDesktopPosition(x, y, w, h, updateChildren); + desktopX = x; + desktopY = y; + desktopW = w; + desktopH = h; + } } } @@ -709,7 +810,16 @@ static bool ProcessKeyMessage(Window window, uint keyCode, int release, XKeyEven if(key != leftControl && key != rightControl && event->state & ControlMask) code.ctrl = true; if(key != leftAlt && key != rightAlt && event->state & Mod1Mask) + { + if(fullScreenMode && key == tab) + { + XUngrabKeyboard(xGlobalDisplay, CurrentTime); + guiApp.SetAppFocus(false); + SetNETWMState((X11Window)window.windowHandle, true, remove, atoms[_net_wm_state_fullscreen], 0); + XIconifyWindow(xGlobalDisplay, (X11Window)window.windowHandle, DefaultScreen(xGlobalDisplay)); + } code.alt = true; + } #ifdef __APPLE__ if(key != leftAlt && key != rightAlt && event->state & (1<<13)) @@ -792,12 +902,12 @@ static uint E_CALL XEventThread(void * data) } */ -static Bool EventChecker(void *display, XEvent *event, char * data) +static X11Bool EventChecker(void *display, XEvent *event, char * data) { return (!data || (event->type == (int) data)) && event->type != NoExpose && event->type != GraphicsExpose; } -static Bool ConfigureNotifyChecker(void *display, XConfigureEvent *event, char * data) +static X11Bool ConfigureNotifyChecker(void *display, XConfigureEvent *event, char * data) { return (!data || (event->window == (X11Window) data)) && event->type == ConfigureNotify; } @@ -891,7 +1001,9 @@ static int MyXErrorHandler(X11Display * display, XErrorEvent * event) char buffer[1024]; if(xGlobalDisplay) XGetErrorText(xGlobalDisplay, event->error_code, buffer, sizeof(buffer)); +#ifdef _DEBUG Logf("X Error: %s\n", buffer); +#endif return 0; } @@ -950,6 +1062,112 @@ static void WaitForViewableWindow(Window window) } } +static bool RequestFrameExtents(X11Window windowHandle) +{ + if(frameExtentSupported != broken) + { + // Request decoration frame extents + XClientMessageEvent event = { 0 }; + event.type = ClientMessage; + event.message_type = atoms[_net_request_frame_extents]; + event.display = xGlobalDisplay; + event.serial = 0; + event.window = windowHandle; + event.send_event = 1; + event.format = 32; + + if(frameExtentSupported == unknown && !frameExtentRequest) + { + frameExtentRequest = GetTime(); + frameExtentWindow = windowHandle; + } + + XSendEvent(xGlobalDisplay, DefaultRootWindow(xGlobalDisplay), bool::false, + SubstructureRedirectMask | SubstructureNotifyMask, (union _XEvent *)&event); + return true; + } + return false; +} + +static bool GetFrameExtents(Window window, bool update) +{ + XWindowData windowData = window.windowData; + bool result = false; + int format; + unsigned long len, fill; + Atom type; + char * data = null; + + if(XGetWindowProperty(xGlobalDisplay, (X11Window)window.windowHandle, + atoms[_net_frame_extents], 0, 4, + False, XA_CARDINAL, &type, &format, &len, + &fill, &data) == Success && data) + { + long *extents = (long *)data; + bool change = extents[0] != windowData.decor.left || + extents[1] != windowData.decor.right || + extents[2] != windowData.decor.top || + extents[3] != windowData.decor.bottom; + + bool hadFrameExtents = windowData.gotFrameExtents; + Box oldDecor = windowData.decor; + + frameExtentSupported = working; + frameExtentWindow = 0; + frameExtentRequest = 0; + + if(!hadFrameExtents || extents[0] || extents[1] || extents[2] || extents[3]) + { + windowData.decor = + { + left = (int)extents[0], right = (int)extents[1], + top = (int)extents[2], bottom = (int)extents[3] + }; + windowData.gotFrameExtents = true; + if(update && change && ((Window)window).clientSize.w > 0) + { + int x = window.position.x, y = window.position.y, w = window.size.w, h = window.size.h; + if(!hadFrameExtents && window.state != maximized) + { + window.ComputeAnchors( + window.normalAnchor, + window.normalSizeAnchor, + &x, &y, &w, &h); + } + else + { + x += windowData.decor.left - oldDecor.left; + y += windowData.decor.top - oldDecor.top; + + w += windowData.decor.left - oldDecor.left + windowData.decor.right - oldDecor.right; + h += windowData.decor.top - oldDecor.top + windowData.decor.bottom - oldDecor.bottom; + } + + if(window.state != maximized) + { + window.Position(x, y, w, h, true, true, true, true, false, !hadFrameExtents && window.state != maximized); + XInterface::UpdateRootWindow(window); + } + } + result = true; + } + XFree(data); + } + return result; +} + +static bool WaitForFrameExtents(Window window) +{ + int attempts = 0; + //XFlush(xGlobalDisplay); + while(attempts++ < 10) + { + if(GetFrameExtents(window, false)) return true; + Sleep(1.0 / RESOLUTION); + } + return false; +} + /**************************************************************************** /// DRIVER IMPLEMENTATION ///////////// ****************************************************************************/ @@ -1014,7 +1232,7 @@ static void SigIntHandler(int value) } /* struct shmid_ds info; - int maxid = shmctl (0, SHM_INFO, &info); + int maxid = shmctl (0, SHM_INFO, &info); int pid = getpgrp(); int thisPid = getpid(); //if(thisPid == pid) @@ -1023,11 +1241,11 @@ static void SigIntHandler(int value) if(maxid >= 0) { int id; - for(id = 0; id <= maxid; id++) + for(id = 0; id <= maxid; id++) { - struct shmid_ds shmseg; + struct shmid_ds shmseg; int shmid; - if((shmid = shmctl(id, SHM_STAT, &shmseg)) >= 0) + if((shmid = shmctl(id, SHM_STAT, &shmseg)) >= 0) { if(shmseg.shm_cpid == pid || shmseg.shm_cpid == thisPid) { @@ -1062,13 +1280,24 @@ class XInterface : Interface #endif xTerminate = false; xGlobalDisplay = XOpenDisplay(null); + // XSynchronize(xGlobalDisplay, True); frameExtentSupported = unknown; - joystickFD[0] = open("/dev/js0", O_RDONLY); + joystickFD[0] = open("/dev/js0", O_RDONLY); joystickFD[1] = open("/dev/js1", O_RDONLY); joystickFD[2] = open("/dev/js2", O_RDONLY); joystickFD[3] = open("/dev/js3", O_RDONLY); + systemCursors[iBeam] = XCreateFontCursor(xGlobalDisplay, XC_xterm); + systemCursors[cross] = XCreateFontCursor(xGlobalDisplay, XC_tcross); + systemCursors[moving] = XCreateFontCursor(xGlobalDisplay, XC_fleur); + systemCursors[sizeNESW] = XCreateFontCursor(xGlobalDisplay, XC_bottom_left_corner); + systemCursors[sizeNS] = XCreateFontCursor(xGlobalDisplay, XC_sb_v_double_arrow); + systemCursors[sizeNWSE] = XCreateFontCursor(xGlobalDisplay, XC_bottom_right_corner); + systemCursors[sizeWE] = XCreateFontCursor(xGlobalDisplay, XC_sb_h_double_arrow); + systemCursors[hand] = XCreateFontCursor(xGlobalDisplay, XC_hand2); + systemCursors[arrow] = XCreateFontCursor(xGlobalDisplay, XC_left_ptr); + if(xGlobalDisplay) { XWindowAttributes attributes = { 0 }; @@ -1146,14 +1375,35 @@ class XInterface : Interface XInternAtoms(xGlobalDisplay, (char**)atomNames, AtomIdents::enumSize, False, atoms); + // Check which atoms are supported by the WM { - Atom protocols[2] = { atoms[wm_delete_window], atoms[wm_take_focus] }; + int format; + unsigned long count, fill; + Atom type; + Atom * data; - XSetWMProtocols(xGlobalDisplay, DefaultRootWindow(xGlobalDisplay), protocols, 2); + if(XGetWindowProperty(xGlobalDisplay, DefaultRootWindow(xGlobalDisplay), atoms[_net_supported], + 0, 10000, False, XA_ATOM, &type, &format, &count, &fill, (void *)&data) == Success) + { + int i; + for (i = 0; i < count; i++) + { + AtomIdents j; + for(j = 0; j < AtomIdents::enumSize; j++) + { + if(atoms[j] == data[i]) + { + atomsSupported[j] = true; + break; + } + } + } + XFree(data); + } } /* - if(atoms[_net_workarea] == None) + if(atomsSupported[_net_workarea]) printf("Warning: _NET_WORKAREA extension not supported\n"); */ @@ -1253,6 +1503,16 @@ class XInterface : Interface delete timerThread; hiResTimer.Stop(); + XFreeCursor(xGlobalDisplay, systemCursors[iBeam]); + XFreeCursor(xGlobalDisplay, systemCursors[cross]); + XFreeCursor(xGlobalDisplay, systemCursors[moving]); + XFreeCursor(xGlobalDisplay, systemCursors[sizeNESW]); + XFreeCursor(xGlobalDisplay, systemCursors[sizeNS]); + XFreeCursor(xGlobalDisplay, systemCursors[sizeNWSE]); + XFreeCursor(xGlobalDisplay, systemCursors[sizeWE]); + XFreeCursor(xGlobalDisplay, systemCursors[hand]); + XFreeCursor(xGlobalDisplay, systemCursors[arrow]); + //XPutBackEvent(xGlobalDisplay, &e); // xThread.Wait(); // delete xThread; @@ -1308,10 +1568,34 @@ class XInterface : Interface case KeyPress: { XKeyEvent * event = (XKeyEvent *) thisEvent; + incref window; + timeStamp = event->time; + if(!window.active) + { + Window modalRoot = window.FindModal(); + XWindowData windowData; + + activeWindow = (X11Window)window.windowHandle; + + if(!window.parent || window != window.parent.activeChild) + { + if(modalRoot) + modalRoot.ExternalActivate(true, true, window, null); + else + window.ExternalActivate(true, true, window, null); // lastActive); + windowData = modalRoot ? modalRoot.windowData : window.windowData; + if(windowData && windowData.ic) + { + // XSetICValues(ic, XNClientWindow, window.windowHandle, XNFocusWindow, window.windowHandle, 0); + XSetICFocus(windowData.ic); + } + } + } //*XUnlockDisplay(xGlobalDisplay); ProcessKeyMessage(window, event->keycode, (event->keycode == lastKeyCode) ? 2 : 0, event); //*if(xGlobalDisplay) XLockDisplay(xGlobalDisplay); lastKeyCode = event->keycode; + delete window; break; } case KeyRelease: @@ -1319,6 +1603,7 @@ class XInterface : Interface XKeyEvent * event = (XKeyEvent *) thisEvent; XEvent nextEvent; lastKeyCode = 0; + timeStamp = event->time; if(!autoRepeatDetectable && XCheckIfEvent(xGlobalDisplay, (XEvent *)&nextEvent, EventChecker, (void *)KeyPress)) { if(im && XFilterEvent(&nextEvent, None)) @@ -1359,9 +1644,16 @@ class XInterface : Interface uint button, buttonDouble, whichButton; uint buttonMask; int x = event->x_root, y = event->y_root; - + timeStamp = event->time; if(event->button == Button1) { + // Force a raise on click here to deal with confused active state preventing to bring the window up + if(!fullScreenMode) + { + if(!atomsSupported[_net_active_window] && !window.isRemote) + XRaiseWindow(xGlobalDisplay, (X11Window)window.windowHandle); + XSetInputFocus(xGlobalDisplay, (X11Window)window.windowHandle, RevertToParent, CurrentTime); + } button = __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnLeftButtonDown; buttonDouble = __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnLeftDoubleClick; whichButton = 0; @@ -1448,6 +1740,7 @@ class XInterface : Interface button = __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnMiddleButtonUp; buttonMask = Button2Mask; } + timeStamp = event->time; if(!(event->state & buttonMask)) break; if(event->state & ShiftMask) keyFlags.shift = true; if(event->state & ControlMask) keyFlags.ctrl = true; @@ -1479,6 +1772,7 @@ class XInterface : Interface // if(event->time - lastTime > 15) { Modifiers keyFlags = 0; + timeStamp = event->time; // int x = event->x_root, y = event->y_root; if(event->state & ShiftMask) keyFlags.shift = true; @@ -1501,23 +1795,18 @@ class XInterface : Interface { XExposeEvent * event = (XExposeEvent *) thisEvent; Box box; - box.left = event->x - window.clientStart.x; - box.top = event->y - window.clientStart.y; + box.left = event->x; + box.top = event->y; box.right = box.left + event->width - 1; box.bottom = box.top + event->height - 1; - window.Update(box); - box.left += window.clientStart.x; - box.top += window.clientStart.y; - box.right += window.clientStart.x; - box.bottom += window.clientStart.y; window.UpdateDirty(box); break; } case SelectionRequest: { - XSelectionRequestEvent *req = (XSelectionRequestEvent *) thisEvent; - XEvent respond; - if(req->target == atoms[targets] && clipBoardData) + XSelectionRequestEvent *req = (XSelectionRequestEvent *) thisEvent; + XEvent respond; + if(req->target == atoms[targets] && clipBoardData) { Atom * supportedTargets = new Atom[4]; supportedTargets[0] = atoms[targets]; @@ -1525,27 +1814,27 @@ class XInterface : Interface supportedTargets[2] = XA_STRING; supportedTargets[3] = atoms[utf8_string]; XChangeProperty(xGlobalDisplay,req->requestor, req->_property, - XA_ATOM,32,PropModeReplace, (byte *) supportedTargets, 4*sizeof(Atom)); + XA_ATOM,32,PropModeReplace, (byte *) supportedTargets, 4*sizeof(Atom)); respond.xselection._property = req->_property; delete supportedTargets; } else if((req->target == XA_STRING || req->target == atoms[utf8_string]) && clipBoardData) - { + { Atom _property = (req->_property == None) ? req->target : req->_property; - XChangeProperty(xGlobalDisplay,req->requestor, _property, - req->target/*req->_property*/,8,PropModeReplace, (byte *) clipBoardData, strlen(clipBoardData)); - respond.xselection._property = _property; - } + XChangeProperty(xGlobalDisplay,req->requestor, _property, + req->target/*req->_property*/,8,PropModeReplace, (byte *) clipBoardData, strlen(clipBoardData)); + respond.xselection._property = _property; + } else - respond.xselection._property = None; + respond.xselection._property = None; respond.xselection.type = SelectionNotify; - respond.xselection.display = req->display; - respond.xselection.requestor = req->requestor; - respond.xselection.selection =req->selection; - respond.xselection.target = req->target; - respond.xselection.time = CurrentTime; - XSendEvent(xGlobalDisplay, req->requestor,0,0,&respond); + respond.xselection.display = req->display; + respond.xselection.requestor = req->requestor; + respond.xselection.selection =req->selection; + respond.xselection.target = req->target; + respond.xselection.time = CurrentTime; + XSendEvent(xGlobalDisplay, req->requestor,0,0,&respond); break; } case SelectionClear: @@ -1555,6 +1844,18 @@ class XInterface : Interface } case FocusIn: { + guiApp.SetAppFocus(true); + + if(fullScreenMode) + { + XRaiseWindow(xGlobalDisplay, (X11Window)window.windowHandle); + SetNETWMState((X11Window)window.windowHandle, true, add, atoms[_net_wm_state_fullscreen], 0); + XGrabKeyboard(xGlobalDisplay, (X11Window)window.windowHandle, False, GrabModeAsync, GrabModeAsync, CurrentTime); + (xGlobalDisplay, (X11Window)window.windowHandle, RevertToParent, timeStamp); + XInterface::UpdateRootWindow(window); + break; + } + if(activeWindow != (X11Window)window.windowHandle) { XFocusChangeEvent *event = (XFocusChangeEvent *) thisEvent; @@ -1587,6 +1888,18 @@ class XInterface : Interface } case FocusOut: { + if((X11Window)window.windowHandle == activeWindow) + guiApp.SetAppFocus(false); + + if(fullScreenMode) + { + SetNETWMState((X11Window)window.windowHandle, true, remove, atoms[_net_wm_state_fullscreen], 0); + XUngrabKeyboard(xGlobalDisplay, CurrentTime); + // -- This XIconifyWindow causes trouble on Gnome Classic + XIconifyWindow(xGlobalDisplay, (X11Window)window.windowHandle, DefaultScreen(xGlobalDisplay)); + break; + } + #ifdef _DEBUG //printf("Processing a FocusOut Event for %s (%x)\n", window._class.name, window); #endif @@ -1666,7 +1979,7 @@ class XInterface : Interface #endif { XFocusChangeEvent *event = (XFocusChangeEvent *) thisEvent; - if(window != window.parent.activeChild && window != guiApp.interimWindow) break; + if(window.parent && window != window.parent.activeChild && window != guiApp.interimWindow) break; incref window; #ifdef _DEBUG @@ -1690,11 +2003,12 @@ class XInterface : Interface case ConfigureNotify: { XConfigureEvent * event = (XConfigureEvent *) thisEvent; + bool unmaximized = false; + if(!window.visible || fullScreenMode) break; while(XCheckIfEvent(xGlobalDisplay, (XEvent *)thisEvent, (void *)ConfigureNotifyChecker, (void *)window.windowHandle)); //if(event->x - desktopX != window.position.x || event->y - desktopY != window.position.y || event->width != window.size.w || event->height != window.size.h) - // TODO: Support _NET_REQUEST_FRAME_EXTENTS message / _NET_FRAME_EXTENTS property for decoration size awareness - if(window.nativeDecorations) + if(atomsSupported[_net_wm_state]) //window.nativeDecorations) { int format; unsigned long len, fill; @@ -1720,50 +2034,98 @@ class XInterface : Interface if(maxVert && maxHorz) { if(window.state != maximized) + { *&window.state = maximized; + if(!window.nativeDecorations) + window.CreateSystemChildren(); + } } else if(isMinimized) { if(window.state != minimized) + { *&window.state = minimized; + if(!window.nativeDecorations) + window.CreateSystemChildren(); + } } else if(window.state != normal) + { + if(window.state == maximized) + unmaximized = true; *&window.state = normal; + if(!window.nativeDecorations) + window.CreateSystemChildren(); + } } } { - int x = event->x; - int y = event->y; - int w = event->width, h = event->height; + bool offset = false; + int x, y, w, h; + if(unmaximized && window.nativeDecorations) + { + if(window.nativeDecorations && RequestFrameExtents((X11Window)window.windowHandle)) + WaitForFrameExtents(window); - //if(event->send_event) + // Ensure we set the normal size anchor when un-maximizing + window.ComputeAnchors(window.normalAnchor, window.normalSizeAnchor, &x, &y, &w, &h); + } + else { - X11Window rootChild; - int rootX, rootY; - XTranslateCoordinates(xGlobalDisplay, event->window, - RootWindow(xGlobalDisplay, DefaultScreen(xGlobalDisplay)), 0, 0, - &rootX, &rootY, &rootChild); - x = rootX; - y = rootY; + x = event->x; + y = event->y; + w = event->width, h = event->height; + + //if(event->send_event) + { + X11Window rootChild; + int rootX, rootY; + XTranslateCoordinates(xGlobalDisplay, event->window, + RootWindow(xGlobalDisplay, DefaultScreen(xGlobalDisplay)), 0, 0, + &rootX, &rootY, &rootChild); + + if(x != rootX || y != rootY) + { + /*if(event->send_event) + offset = true;*/ + x = rootX; + y = rootY; + } + } + + x -= desktopX; + y -= desktopY; + + if(window.nativeDecorations && window.state != maximized) + { + x -= windowData.decor.left; + y -= windowData.decor.top; + w += windowData.decor.left + windowData.decor.right; + h += windowData.decor.top + windowData.decor.bottom; + + /* + x -= window.clientStart.x; + y -= window.clientStart.y - (window.hasMenuBar ? skinMenuHeight : 0); + w += window.size.w - window.clientSize.w; + h += window.size.h - window.clientSize.h; + */ + } } - x -= desktopX; - y -= desktopY; + window.Position(x, y, w, h, true, true, true, true, false, unmaximized); - if(window.nativeDecorations && window.state != maximized) + // Break the anchors for moveable/resizable windows + // Avoid doing this if the translation wasn't in sync as it will cause the window to move around + if(!unmaximized && !offset && window.style.fixed && window.state == normal) { - x -= windowData.decor.left; - y -= windowData.decor.top; - w += windowData.decor.left + windowData.decor.right; - h += windowData.decor.top + windowData.decor.bottom; - /* - x -= window.clientStart.x; - y -= window.clientStart.y - (window.hasMenuBar ? skinMenuHeight : 0); - w += window.size.w - window.clientSize.w; - h += window.size.h - window.clientSize.h; - */ + window.normalAnchor = Anchor + { + left = x + windowData.decor.left, + top = y + windowData.decor.top + }; + window.normalSizeAnchor = + SizeAnchor { { window.clientSize.w, window.clientSize.h }, isClientW = true, isClientH = true }; } - window.Position(x, y, w, h, true, true, true, true, false, false); } break; } @@ -1781,9 +2143,19 @@ class XInterface : Interface Window modalRoot; XWindowData windowData; bool laterFocus; - activeWindow = (X11Window)window.windowHandle; - timeStamp = (int)event->data.l[1]; + if(fullScreenMode) + { + XRaiseWindow(xGlobalDisplay, (X11Window)window.windowHandle); + XSetInputFocus(xGlobalDisplay, (X11Window)window.windowHandle, RevertToParent, timeStamp); + XGrabKeyboard(xGlobalDisplay, (X11Window)window.windowHandle, False, GrabModeAsync, GrabModeAsync, CurrentTime); + guiApp.SetAppFocus(true); + break; + } + + //activeWindow = (X11Window)window.windowHandle; + + timeStamp = (X11Time)event->data.l[1]; windowData = window.windowData; laterFocus = windowData.laterFocus; @@ -1804,7 +2176,7 @@ class XInterface : Interface XFocusChangeEvent *event = (XFocusChangeEvent *) &checkEvent; Window window; XFindContext(xGlobalDisplay, event->window, windowContext, (XPointer *) &window); - if(window != window.parent.activeChild) break; + if(window.parent && window != window.parent.activeChild) break; incref window; #ifdef _DEBUG @@ -1830,7 +2202,7 @@ class XInterface : Interface { XRaiseWindow(xGlobalDisplay, (X11Window)modalRoot.windowHandle); WaitForViewableWindow(modalRoot); - if(atoms[_net_active_window]) + if(atomsSupported[_net_active_window]) { XClientMessageEvent event = { 0 }; event.type = ClientMessage; @@ -1840,10 +2212,10 @@ class XInterface : Interface event.window = (X11Window)modalRoot.windowHandle; event.send_event = 1; event.format = 32; - event.data.l[0] = 0; + event.data.l[0] = /*0*/ 1; + event.data.l[1] = timeStamp; + event.data.l[2] = activeWindow; /* - event.data.l[0] = 1; - event.data.l[1] = atoms[_net_wm_user_time]; event.data.l[2] = activeWindow; //guiApp.desktop.activeChild.windowHandle; */ #ifdef _DEBUG @@ -1851,7 +2223,9 @@ class XInterface : Interface #endif XSendEvent(xGlobalDisplay, DefaultRootWindow(xGlobalDisplay), bool::false, SubstructureRedirectMask | SubstructureNotifyMask, (union _XEvent *)&event); - XSetInputFocus(xGlobalDisplay, (X11Window)modalRoot.windowHandle, RevertToPointerRoot, (uint)timeStamp); + XSetInputFocus(xGlobalDisplay, (X11Window)modalRoot.windowHandle, RevertToParent, timeStamp); + guiApp.SetAppFocus(true); + activeWindow = (X11Window)window.windowHandle; //XFlush(xGlobalDisplay); //printf("Done.\n"); @@ -1859,7 +2233,9 @@ class XInterface : Interface } else { - XSetInputFocus(xGlobalDisplay, (X11Window)window.windowHandle, RevertToPointerRoot, (uint)timeStamp); + XSetInputFocus(xGlobalDisplay, (X11Window)window.windowHandle, RevertToParent, timeStamp); + guiApp.SetAppFocus(true); + activeWindow = (X11Window)window.windowHandle; window.ExternalActivate(true, true, window, null); // lastActive); if(windowData && windowData.ic) { @@ -1881,69 +2257,10 @@ class XInterface : Interface { XWindowData windowData = window.windowData; XPropertyEvent * event = (XPropertyEvent *) thisEvent; - if(event->atom == atoms[_net_frame_extents] && + if(!fullScreenMode && event->atom == atoms[_net_frame_extents] && event->state == PropertyNewValue && windowData) { - int format; - unsigned long len, fill; - Atom type; - char * data = null; - - if(XGetWindowProperty(xGlobalDisplay, (X11Window)window.windowHandle, - atoms[_net_frame_extents], 0, 4, - False, XA_CARDINAL, &type, &format, &len, - &fill, &data) == Success && data) - { - long *extents = (long *)data; - bool change = extents[0] != windowData.decor.left || - extents[1] != windowData.decor.right || - extents[2] != windowData.decor.top || - extents[3] != windowData.decor.bottom; - - bool hadFrameExtents = windowData.gotFrameExtents; - Box oldDecor = windowData.decor; - - frameExtentSupported = working; - frameExtentWindow = 0; - frameExtentRequest = 0; - - if(!hadFrameExtents || extents[0] || extents[1] || extents[2] || extents[3]) - { - windowData.decor = - { - left = (int)extents[0], right = (int)extents[1], - top = (int)extents[2], bottom = (int)extents[3] - }; - windowData.gotFrameExtents = true; - if(change && ((Window)window).clientSize.w > 0) - { - int x = window.position.x, y = window.position.y, w = window.size.w, h = window.size.h; - if(!hadFrameExtents && window.state != maximized) - { - window.ComputeAnchors( - window.normalAnchor, - window.normalSizeAnchor, - &x, &y, &w, &h); - } - else - { - x += windowData.decor.left - oldDecor.left; - y += windowData.decor.top - oldDecor.top; - - w += windowData.decor.left - oldDecor.left + windowData.decor.right - oldDecor.right; - h += windowData.decor.top - oldDecor.top + windowData.decor.bottom - oldDecor.bottom; - } - - if(window.state != maximized) - { - window.Position(x, y, w, h, true, true, true, true, false, !hadFrameExtents && window.state != maximized); - UpdateRootWindow(window); - } - } - } - XFree(data); - } - else + if(!GetFrameExtents(window, true)) windowData.gotFrameExtents = true; // Unsupported? } break; @@ -2027,7 +2344,10 @@ class XInterface : Interface XIC ic = null; unsigned long mask = EVENT_MASK; - attributes.override_redirect = window.interim ? True : False; + // Old WM (e.g. TWM), use built-in decorations + if(!atomsSupported[_net_wm_state]) + window.nativeDecorations = false; + attributes.override_redirect = (window.interim || (!atomsSupported[_net_wm_state] && !window.nativeDecorations)) ? True : False; attributes.event_mask = EVENT_MASK; //printf("%s\n", guiApp.defaultDisplayDriver); #if !defined(ECERE_VANILLA) && !defined(ECERE_NO3D) && !defined(ECERE_NOGL) @@ -2144,9 +2464,50 @@ class XInterface : Interface if(fullScreenMode) { - windowHandle = XCreateWindow(xGlobalDisplay, DefaultRootWindow(xGlobalDisplay), - 0,0,guiApp.desktop.size.w,guiApp.desktop.size.h,0, depth, InputOutput, visual ? visual : CopyFromParent, - CWEventMask | (visual ? (CWColormap | CWBorderPixel) : 0)/*| CWOverrideRedirect*/, &attributes); + windowHandle = XCreateWindow(xGlobalDisplay, + DefaultRootWindow(xGlobalDisplay), + 0,0, + XDisplayWidth(xGlobalDisplay, DefaultScreen(xGlobalDisplay)), + XDisplayHeight(xGlobalDisplay, DefaultScreen(xGlobalDisplay)), + 0, depth, InputOutput, visual ? visual : CopyFromParent, + CWEventMask | (visual ? (CWColormap | CWBorderPixel) : 0) | CWOverrideRedirect, + &attributes); + + { + XSizeHints hints = { 0 }; + XSetWMNormalHints(xGlobalDisplay, windowHandle, &hints); + } + + { + String caption = window.caption; + XChangeProperty(xGlobalDisplay, windowHandle, atoms[_net_wm_name], + atoms[utf8_string], 8, PropModeReplace, (byte *)window.caption, caption ? strlen(caption) : 0); + XChangeProperty(xGlobalDisplay, windowHandle, atoms[wm_name], + atoms[utf8_string], 8, PropModeReplace, (byte *)window.caption, caption ? strlen(caption) : 0); + } + + SetNETWMState((X11Window)windowHandle, false, add, atoms[_net_wm_state_fullscreen], 0); + //SetNETWMState((X11Window)windowHandle, false, add, atoms[_net_wm_state_above], 0); + { + Atom hints[4]; + int count; + + hints[0] = atoms[_net_wm_window_type_normal]; + count = 1; + XChangeProperty(xGlobalDisplay, windowHandle, atoms[_net_wm_window_type], XA_ATOM, 32, + PropModeReplace, (unsigned char*)&hints, count); + } + + { + XWMHints xwmHints; + xwmHints.flags = InputHint; + xwmHints.input = 0; + XSetWMHints(xGlobalDisplay, windowHandle, &xwmHints); + } + { + Atom protocols[2] = { atoms[wm_delete_window], atoms[wm_take_focus] }; + XSetWMProtocols(xGlobalDisplay, windowHandle, protocols, 2); + } } /* Unsupported for now... @@ -2162,6 +2523,26 @@ class XInterface : Interface else { X11Window parentWindow = (X11Window)null; + int x = window.position.x + desktopX, y = window.position.y + desktopY; + int w = window.state == normal ? Max(1, window.size.w) : Max(1, window.normalSizeAnchor.size.w); + int h = window.state == normal ? Max(1, window.size.h) : Max(1, window.normalSizeAnchor.size.h); + MinMaxValue smw = 0, smh = 0; + MinMaxValue minW = window.minSize.w, minH = window.minSize.h; + window.OnResizing((int *)&minW, (int *)&minH); + + // To fix jumping message boxes on Cinnamon: + if(window.state == normal && (minW > window.minSize.w || minH > window.minSize.w)) + window.ComputeAnchors(window.normalAnchor, window.normalSizeAnchor, &x, &y, &w, &h); + + window.SetWindowMinimum(&smw, &smh); + minW = Max(minW, smw); + minH = Max(minH, smh); + + if(!window.nativeDecorations && window.state != normal) + { + w += window.size.w - window.clientSize.w; + h += window.size.h - window.clientSize.h; + } if(window.master.rootWindow && window.master.rootWindow != guiApp.desktop && (window._isModal || window.style.interim)) { @@ -2176,7 +2557,8 @@ class XInterface : Interface parentWindow = (X11Window)null; windowHandle = XCreateWindow(xGlobalDisplay, DefaultRootWindow(xGlobalDisplay), - 0,0,1,1,0, depth, InputOutput, visual ? visual : CopyFromParent, + x, y, w, h, + 0, depth, InputOutput, visual ? visual : CopyFromParent, CWEventMask | CWOverrideRedirect | (visual ? (CWColormap | CWBorderPixel) : 0), &attributes); if(parentWindow && (window.interim || window.isModal)) @@ -2187,25 +2569,37 @@ class XInterface : Interface //printf("Done.\n"); //XChangeProperty(xGlobalDisplay, windowHandle, atoms[wm_transient_for], XA_WINDOW, 32, PropModeReplace, (unsigned char*)&parentWindow, 1); if(window.isModal) - { - Atom hints[1] = { atoms[_net_wm_state_modal] }; - XChangeProperty(xGlobalDisplay, windowHandle, atoms[_net_wm_state], XA_ATOM, 32, PropModeReplace, (unsigned char*)&hints, 1); - } + SetNETWMState(windowHandle, false, add, atoms[_net_wm_state_modal], 0); } { - Atom hints[2] = + Atom hints[4]; + int count; + if(parentWindow && window.interim) + { + hints[0] = atoms[_net_wm_window_type_dropdown_menu]; + hints[1] = atoms[_net_wm_window_type_popup_menu]; + hints[2] = atoms[_net_wm_window_type_menu]; + count = 3; + } + else if(parentWindow) { - parentWindow ? atoms[_net_wm_window_type_menu] : atoms[_net_wm_window_type_normal], - parentWindow ? atoms[_net_wm_window_type_popup_menu] : 0 + hints[0] = atoms[_net_wm_window_type_normal]; + SetNETWMState(windowHandle, false, add, atoms[_net_wm_state_skip_taskbar], 0); + + // Some WMs won't show a close button if dialog is set + // Additionally, this casues jumping of all dialog windows on Cinnamon + //hints[0] = atoms[_net_wm_window_type_dialog]; + count = 1; + } + else + { + hints[0] = atoms[_net_wm_window_type_normal]; + count = 1; }; -#if defined(__APPLE__) || defined(__FreeBSD__) - // Don't set this on non-interim windows for OS X... - if(parentWindow && window.interim) -#endif + XChangeProperty(xGlobalDisplay, windowHandle, atoms[_net_wm_window_type], XA_ATOM, 32, + PropModeReplace, (unsigned char*)&hints, count); - XChangeProperty(xGlobalDisplay, windowHandle, atoms[_net_wm_window_type], XA_ATOM, 32, - PropModeReplace, (unsigned char*)&hints, parentWindow ? 2 : 1); { XWMHints xwmHints; xwmHints.flags = InputHint; @@ -2216,6 +2610,30 @@ class XInterface : Interface Atom protocols[2] = { atoms[wm_delete_window], atoms[wm_take_focus] }; XSetWMProtocols(xGlobalDisplay, windowHandle, protocols, 2); } + + // Set Normal hints for minimum/maximum size + { + XSizeHints hints = { 0 }; + hints.min_width = minW; + hints.min_height = minH; + hints.flags |= PMinSize; + + if(window.maxSize.w < MAXINT || window.minSize.h < MAXINT) + { + hints.max_width = window.maxSize.w; + hints.max_height = window.maxSize.h; + hints.flags |= PMaxSize; + } + hints.x = x; + hints.y = y; + hints.flags |= PPosition; + + hints.width = w; + hints.height = h; + hints.flags |= PSize; + + XSetWMNormalHints(xGlobalDisplay, windowHandle, &hints); + } } } } @@ -2256,8 +2674,8 @@ class XInterface : Interface } if(ic) { - XGetICValues(ic, XNFilterEvents, &mask, NULL); - mask |= EVENT_MASK; + XGetICValues(ic, XNFilterEvents, &mask, NULL); + mask |= EVENT_MASK; } /* XSelectInput(xGlobalDisplay, windowHandle, mask); @@ -2277,16 +2695,18 @@ class XInterface : Interface { (window.nativeDecorations ? 0 : MWM_HINTS_DECORATIONS)|MWM_HINTS_FUNCTIONS, (window.hasClose ? MWM_FUNC_CLOSE : 0) | - (window.hasMaximize ? MWM_FUNC_MAXIMIZE : 0) | - (window.hasMinimize ? MWM_FUNC_MINIMIZE : 0) | - ((window.moveable || ((BorderBits)window.borderStyle).fixed) ? MWM_FUNC_MOVE : 0) | - (((BorderBits)window.borderStyle).sizable ? MWM_FUNC_RESIZE : 0), + (fullScreenMode || window.hasMaximize ? MWM_FUNC_MAXIMIZE : 0) | + (fullScreenMode || window.hasMinimize ? MWM_FUNC_MINIMIZE : 0) | + ((fullScreenMode || window.moveable || ((BorderBits)window.borderStyle).fixed) ? MWM_FUNC_MOVE : 0) | + (fullScreenMode || ((BorderBits)window.borderStyle).sizable ? MWM_FUNC_RESIZE : 0), 0, 0, 0 }; XChangeProperty(xGlobalDisplay, windowHandle, atoms[_motif_wm_hints], atoms[_motif_wm_hints], 32, PropModeReplace, (unsigned char*)&hints, 5); } - if(atoms[_net_wm_pid] != None) + + // *** We set this for ourselves, so don't check atomsSupported !!! *** + if(atoms[_net_wm_pid]) { int pid = getpid(); // printf("Setting _NET_WM_PID to %d\n", pid); @@ -2295,16 +2715,12 @@ class XInterface : Interface } } - /* { - Atom protocolsAtom = XInternAtom(xGlobalDisplay, "WM_PROTOCOLS", False); - if ( protocolsAtom != None ) - { - MWM_Hints hints = { MWM_HINTS_DECORATIONS|MWM_HINTS_FUNCTIONS, 0, 0, 0, 0 }; - XChangeProperty(xGlobalDisplay, windowHandle, atoms[_motif_wm_hints], atoms[_motif_wm_hints], 32, - PropModeReplace, (unsigned char*)&hints, sizeof(hints)/4); - } - }*/ + XWMHints wmHints = { 0 }; + wmHints.input = True; + XSetWMHints(xGlobalDisplay, windowHandle, &wmHints); + } + // XFlush(xGlobalDisplay); window.windowData = XWindowData { visualInfo, ic }; @@ -2319,30 +2735,13 @@ class XInterface : Interface XUngrabPointer(xGlobalDisplay, CurrentTime); } - if(window.nativeDecorations && frameExtentSupported != broken) + if(!fullScreenMode && !window.nativeDecorations || !RequestFrameExtents(windowHandle)) + ((XWindowData)window.windowData).gotFrameExtents = true; + if(fullScreenMode) { - // Request decoration frame extents - XClientMessageEvent event = { 0 }; - event.type = ClientMessage; - event.message_type = atoms[_net_request_frame_extents]; - event.display = xGlobalDisplay; - event.serial = 0; - event.window = (X11Window)windowHandle; - event.send_event = 1; - window.windowHandle = (void *)windowHandle; - event.format = 32; - - if(frameExtentSupported == unknown && !frameExtentRequest) - { - frameExtentRequest = GetTime(); - frameExtentWindow = windowHandle; - } - - XSendEvent(xGlobalDisplay, DefaultRootWindow(xGlobalDisplay), bool::false, - SubstructureRedirectMask | SubstructureNotifyMask, (union _XEvent *)&event); + XMapWindow(xGlobalDisplay, windowHandle); + XGrabKeyboard(xGlobalDisplay, windowHandle, False, GrabModeAsync, GrabModeAsync, CurrentTime); } - else - ((XWindowData)window.windowData).gotFrameExtents = true; return (void *)windowHandle; } @@ -2391,7 +2790,7 @@ class XInterface : Interface bool visible = window.visible; if(window.visible && window.created) XMapWindow(xGlobalDisplay, (X11Window)window.windowHandle); - if(window.state == minimized) return; + if(window.state == minimized && atomsSupported[_net_wm_state]) return; if(window.nativeDecorations) { @@ -2400,7 +2799,17 @@ class XInterface : Interface // Was commenting this out was part of #700/#795 fix, but this causes jumping of e.g. About box after getting frame extents PropertyNotify // && window.state != maximized -- required for Cinnamon on Mint 14/15 - if(!windowData.gotFrameExtents && window.state != maximized) return; + if(!windowData.gotFrameExtents && window.state != maximized) + { + if(WaitForFrameExtents(window)) + { + x += windowData.decor.left; + y += windowData.decor.top ; + + w += windowData.decor.left + windowData.decor.right; + h += windowData.decor.top + windowData.decor.bottom; + } + } x -= windowData.decor.left; y -= windowData.decor.top; @@ -2409,26 +2818,37 @@ class XInterface : Interface h -= windowData.decor.top + windowData.decor.bottom; // Tweak for first unmaximize on Unity on Ubuntu 11.10 - if(window.state == maximized && (desktopX + w > desktopW || desktopY + h > desktopH)) + /*if(window.state == maximized && (desktopX + w > desktopW || desktopY + h > desktopH)) { w -= 40; h -= 40; - } + }*/ } x += desktopX; y += desktopY; - if(move && resize) - XMoveResizeWindow(xGlobalDisplay, (X11Window)window.windowHandle, x, y, w, h); - else if(move) - XMoveWindow(xGlobalDisplay, (X11Window)window.windowHandle, x, y); - else if(resize) - XResizeWindow(xGlobalDisplay, (X11Window)window.windowHandle, w, h); -#if defined(__APPLE__) -// if(window.created && !visible) - // XUnmapWindow(xGlobalDisplay, (X11Window)window.windowHandle); -#endif + if(!fullScreenMode && (!atomsSupported[_net_wm_state] || window.state != maximized)) + { + if(move && resize) + XMoveResizeWindow(xGlobalDisplay, (X11Window)window.windowHandle, x, y, w, h); + else if(move) + XMoveWindow(xGlobalDisplay, (X11Window)window.windowHandle, x, y); + else if(resize) + XResizeWindow(xGlobalDisplay, (X11Window)window.windowHandle, w, h); + + // Reset min/max for fixed size windows on WMs not looking at MWM_FUNC_RESIZE (e.g. Cinnamon) + if(window.style.fixed && !window.style.sizable) + { + XSizeHints hints = { 0 }; + long supplied; + XGetWMNormalHints(xGlobalDisplay, (X11Window)window.windowHandle, &hints, &supplied); + hints.min_width = hints.max_width = w; + hints.min_height = hints.max_height = h; + hints.flags |= PMinSize|PMaxSize; + XSetWMNormalHints(xGlobalDisplay, (X11Window)window.windowHandle, &hints); + } + } } } @@ -2469,26 +2889,42 @@ class XInterface : Interface } } - void SetRootWindowState(Window window, WindowState state, bool visible) { + // Old WM (e.g. TWM), use built-in decorations + if(!atomsSupported[_net_wm_state]) + window.nativeDecorations = false; if(!window.parent || !window.parent.display) { + XWindowData windowData = window.windowData; //Logf("Set root window state %d %s\n", state, window.name); if(visible) { - XMapWindow(xGlobalDisplay, (X11Window)window.windowHandle); - WaitForViewableWindow(window); - if(window.creationActivation == activate && state != minimized) - ActivateRootWindow(window); + if(!windowData.currentlyVisible) + { + XMapWindow(xGlobalDisplay, (X11Window)window.windowHandle); + windowData.currentlyVisible = true; + WaitForViewableWindow(window); + if(window.creationActivation == activate && state != minimized) + ActivateRootWindow(window); + } + + if(fullScreenMode && state != minimized) + { + int w = XDisplayWidth(xGlobalDisplay, DefaultScreen(xGlobalDisplay)); + int h = XDisplayHeight(xGlobalDisplay, DefaultScreen(xGlobalDisplay)); + SetNETWMState((X11Window)window.windowHandle, true, add, atoms[_net_wm_state_fullscreen], 0); + XMoveResizeWindow(xGlobalDisplay, (X11Window)window.windowHandle, 0, 0, w, h); - if(state == minimized) + guiApp.SetDesktopPosition(0, 0, w, h, true); + window.Position(0, 0, w, h, true, true, true, true, false, false); + } + + if(state == minimized && atomsSupported[_net_wm_state]) { uint iconic = IconicState; - /* - XChangeProperty(xGlobalDisplay, window.windowHandle, atoms[_net_wm_state], XA_ATOM, 32, - PropModeReplace, (unsigned char*)&atoms[_net_wm_state_hidden], 1); - */ + + // SetNETWMState(window.windowHandle, true, add, atoms[_net_wm_state_hidden], null); /* XChangeProperty(xGlobalDisplay, window.windowHandle, atoms[wm_state], XA_CARDINAL, 32, PropModeReplace, &iconic, 1); @@ -2497,19 +2933,6 @@ class XInterface : Interface /* XClientMessageEvent event = { 0 }; event.type = ClientMessage; - event.message_type = atoms[_net_wm_state]; - event.display = xGlobalDisplay; - event.serial = 0; - event.window = window.windowHandle; - event.send_event = 1; - event.format = 32; - event.data.l[0] = 2; // 1; - event.data.l[1] = atoms[_net_wm_state_hidden]; - XSendEvent(xGlobalDisplay, DefaultRootWindow(xGlobalDisplay), bool::false, SubstructureRedirectMask | SubstructureNotifyMask, &event); - */ - /* - XClientMessageEvent event = { 0 }; - event.type = ClientMessage; event.message_type = atoms[wm_state]; event.display = xGlobalDisplay; event.window = window.windowHandle; @@ -2520,75 +2943,68 @@ class XInterface : Interface */ // printf("Attempting to minimize %s\n", window._class.name); - XIconifyWindow(xGlobalDisplay, (X11Window)window.windowHandle, DefaultScreen(xGlobalDisplay)); + if(!fullScreenMode) + XIconifyWindow(xGlobalDisplay, (X11Window)window.windowHandle, DefaultScreen(xGlobalDisplay)); } - else + else if(!fullScreenMode) { - if(!window.nativeDecorations || (!((XWindowData)window.windowData).gotFrameExtents && window.state == maximized)) //((XWindowData)window.windowData).gotFrameExtents && (!window.nativeDecorations || window.state == state)) + //((XWindowData)window.windowData).gotFrameExtents && (!window.nativeDecorations || window.state == state)) + if(!atomsSupported[_net_wm_state] || (!((XWindowData)window.windowData).gotFrameExtents && window.state == maximized)) { - int x = window.position.x; - int y = window.position.y; - int w = window.size.w; - int h = window.size.h; - - if(window.nativeDecorations) + // Running this block avoids the initial IDE maximized->unmaximized flicker + //if(window.state != maximized || !atomsSupported[_net_wm_state] || window.nativeDecorations) { - XWindowData windowData = window.windowData; - x -= windowData.decor.left; - y -= windowData.decor.top; + int x = window.position.x; + int y = window.position.y; + int w = window.size.w; + int h = window.size.h; + + if(window.nativeDecorations) + { + XWindowData windowData = window.windowData; + x -= windowData.decor.left; + y -= windowData.decor.top; + + w -= windowData.decor.left + windowData.decor.right; + h -= windowData.decor.top + windowData.decor.bottom; + } + x += desktopX; + y += desktopY; - w -= windowData.decor.left + windowData.decor.right; - h -= windowData.decor.top + windowData.decor.bottom; + XMoveResizeWindow(xGlobalDisplay, + (X11Window)window.windowHandle, + x, y, w, h); } - x += desktopX; - y += desktopY; - - // With native decorations, we do it the first time - // or the WM (Gnome) is sticking it to the top/right! - XMoveResizeWindow(xGlobalDisplay, - (X11Window)window.windowHandle, - x, y, w, h); UpdateRootWindow(window); } - if(window.nativeDecorations) + if(atomsSupported[_net_wm_state]) { // Maximize / Restore the window - XClientMessageEvent event = { 0 }; - event.type = ClientMessage; - event.message_type = atoms[_net_wm_state]; - event.display = xGlobalDisplay; - event.serial = 0; - event.window = (X11Window)window.windowHandle; - event.send_event = 1; - event.format = 32; - event.data.l[0] = (state == maximized) ? 1 : 0; - event.data.l[1] = atoms[_net_wm_state_maximized_vert]; - event.data.l[2] = atoms[_net_wm_state_maximized_horz]; - XSendEvent(xGlobalDisplay, DefaultRootWindow(xGlobalDisplay), bool::false, - SubstructureRedirectMask | SubstructureNotifyMask, (union _XEvent *)&event); + SetNETWMState((X11Window)window.windowHandle, true, state == maximized ? add : remove, + atoms[_net_wm_state_maximized_vert], atoms[_net_wm_state_maximized_horz]); + if(state == maximized) + { + // Prevent the code in ConfigureNotify to think the window has been unmaximized + // if the Window Manager hasn't set the hints yet. + XFlush(xGlobalDisplay); + Sleep(0.01); + } } } } else + { XUnmapWindow(xGlobalDisplay, (X11Window)window.windowHandle); + windowData.currentlyVisible = false; + } //XFlush(xGlobalDisplay); } } void FlashRootWindow(Window window) { - XClientMessageEvent event = { 0 }; // printf("Attempting to flash root window\n"); - event.type = ClientMessage; - event.message_type = atoms[_net_wm_state]; - event.display = xGlobalDisplay; - event.serial = 0; - event.window = (X11Window)window.windowHandle; - event.send_event = 1; - event.format = 32; - event.data.l[0] = 1; - event.data.l[1] = atoms[_net_wm_state_demands_attention]; - XSendEvent(xGlobalDisplay, DefaultRootWindow(xGlobalDisplay), bool::false, SubstructureRedirectMask | SubstructureNotifyMask, (union _XEvent *)&event); + SetNETWMState((X11Window)window.windowHandle, true, add, atoms[_net_wm_state_demands_attention], 0); } void ActivateRootWindow(Window window) @@ -2597,11 +3013,16 @@ class XInterface : Interface { if(!window.style.hidden && window.created) { + XWindowData windowData = window.windowData; //printf("Activate root window %s\n", window._class.name); + if(!windowData.currentlyVisible) + { + XMapWindow(xGlobalDisplay, (X11Window)window.windowHandle); + WaitForViewableWindow(window); + windowData.currentlyVisible = true; + } XRaiseWindow(xGlobalDisplay, (X11Window)window.windowHandle); - XMapWindow(xGlobalDisplay, (X11Window)window.windowHandle); - WaitForViewableWindow(window); - if(atoms[_net_active_window]) + if(atomsSupported[_net_active_window]) { XClientMessageEvent event = { 0 }; event.type = ClientMessage; @@ -2611,14 +3032,9 @@ class XInterface : Interface event.window = (X11Window)window.windowHandle; event.send_event = 1; event.format = 32; - event.data.l[0] = 0; - - //event.data.l[0] = 2; - //event.data.l[1] = timeStamp; - - - //event.data.l[1] = atoms[_net_wm_user_time]; - //event.data.l[2] = activeWindow; //guiApp.desktop.activeChild.windowHandle; + event.data.l[0] = /*0*/ 1; + event.data.l[1] = timeStamp; + event.data.l[2] = activeWindow; //guiApp.desktop.activeChild.windowHandle; #ifdef _DEBUG //printf("(ActivateRootWindow) Setting _NET_ACTIVE_WINDOW for %s (%x)\n", window._class.name, window); @@ -2626,11 +3042,11 @@ class XInterface : Interface XSendEvent(xGlobalDisplay, DefaultRootWindow(xGlobalDisplay), bool::false, SubstructureRedirectMask | SubstructureNotifyMask, (union _XEvent *)&event); //#if defined(__APPLE__) - XSetInputFocus(xGlobalDisplay, (X11Window)window.windowHandle, RevertToPointerRoot, CurrentTime); + XSetInputFocus(xGlobalDisplay, (X11Window)window.windowHandle, RevertToParent, CurrentTime); //#endif } else - XSetInputFocus(xGlobalDisplay, (X11Window)window.windowHandle, RevertToPointerRoot, CurrentTime); + XSetInputFocus(xGlobalDisplay, (X11Window)window.windowHandle, RevertToParent, CurrentTime); } } } @@ -2664,7 +3080,7 @@ class XInterface : Interface void SetMousePosition(int x, int y) { - + XWarpPointer(xGlobalDisplay, None, DefaultRootWindow(xGlobalDisplay), 0, 0, 0, 0, x, y); } void SetMouseRange(Window window, Box box) @@ -2676,7 +3092,7 @@ class XInterface : Interface { if(!window.parent || !window.parent.display) { - XMoveResizeWindow(xGlobalDisplay, confineWindow, box.left + desktopX, box.top + desktopY, + XMoveResizeWindow(xGlobalDisplay, confineWindow, box.left /*+ desktopX*/, box.top /*+ desktopY*/, box.right - box.left + 1, box.bottom - box.top + 1); if(!restrictedWindow) @@ -2738,14 +3154,11 @@ class XInterface : Interface // -- Mouse cursor --- - void SetMouseCursor(int cursor) + void SetMouseCursor(Window window, int cursor) { - //*XLockDisplay(xGlobalDisplay); - if(cursor == -1) - { - XDefineCursor(xGlobalDisplay, (X11Window) guiApp.desktop.windowHandle, nullCursor); - } - //*XUnlockDisplay(xGlobalDisplay); + if(window.rootWindow.windowHandle) + XDefineCursor(xGlobalDisplay, (X11Window) window.rootWindow.windowHandle, + cursor == -1 ? nullCursor : systemCursors[(SystemCursor)cursor]); } // --- Caret --- @@ -2776,7 +3189,7 @@ class XInterface : Interface if(clipBoardData) { delete clipBoardData; - XSetSelectionOwner(xGlobalDisplay, atoms[clipboard], None, CurrentTime); + XSetSelectionOwner(xGlobalDisplay, atoms[clipboard], None, CurrentTime); } //*XUnlockDisplay(xGlobalDisplay); } @@ -2964,8 +3377,8 @@ class XInterface : Interface else memcpy(icon + 2, bitmap.picture, bitmap.width * bitmap.height * sizeof(uint32)); XChangeProperty(xGlobalDisplay, (X11Window)window.windowHandle, atoms[_net_wm_icon], - XA_CARDINAL,32,PropModeReplace, (byte *)icon, 2+bitmap.width*bitmap.height); - delete icon; + XA_CARDINAL,32,PropModeReplace, (byte *)icon, 2+bitmap.width*bitmap.height); + delete icon; } delete bitmap; }