mirror of https://github.com/adamdruppe/arsd.git
child window focus requests on x11
This commit is contained in:
parent
155340534c
commit
e1a13734ba
120
simpledisplay.d
120
simpledisplay.d
|
@ -1400,6 +1400,16 @@ enum WindowFlags : int {
|
||||||
Added February 23, 2021 but not yet stabilized.
|
Added February 23, 2021 but not yet stabilized.
|
||||||
+/
|
+/
|
||||||
transient = 64,
|
transient = 64,
|
||||||
|
/++
|
||||||
|
This indicates that the window manages its own platform-specific child window input focus. You must use a delegate, [SimpleWindow.setRequestedInputFocus], to set the input when requested. This delegate returns the handle to the window that should receive the focus.
|
||||||
|
|
||||||
|
This is currently only used for the WM_TAKE_FOCUS protocol on X11 at this time.
|
||||||
|
|
||||||
|
History:
|
||||||
|
Added April 1, 2022
|
||||||
|
+/
|
||||||
|
managesChildWindowFocus = 128,
|
||||||
|
|
||||||
dontAutoShow = 0x1000_0000, /// Don't automatically show window after creation; you will have to call `show()` manually.
|
dontAutoShow = 0x1000_0000, /// Don't automatically show window after creation; you will have to call `show()` manually.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2073,6 +2083,17 @@ class SimpleWindow : CapableOfHandlingNativeEvent, CapableOfBeingDrawnUpon {
|
||||||
_suppressDestruction = true; // so it doesn't try to close
|
_suppressDestruction = true; // so it doesn't try to close
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/++
|
||||||
|
Used iff [WindowFlags.managesChildWindowFocus] is set when the window is created.
|
||||||
|
The delegate will be called when the window manager asks you to take focus.
|
||||||
|
|
||||||
|
This is currently only used for the WM_TAKE_FOCUS protocol on X11 at this time.
|
||||||
|
|
||||||
|
History:
|
||||||
|
Added April 1, 2022 (dub v10.8)
|
||||||
|
+/
|
||||||
|
SimpleWindow delegate() setRequestedInputFocus;
|
||||||
|
|
||||||
/// Experimental, do not use yet
|
/// Experimental, do not use yet
|
||||||
/++
|
/++
|
||||||
Grabs exclusive input from the user until you release it with
|
Grabs exclusive input from the user until you release it with
|
||||||
|
@ -14128,6 +14149,10 @@ mixin DynamicLoad!(XRandr, "Xrandr", 2, XRandrLibrarySuccessfullyLoaded) XRandrL
|
||||||
display = XDisplayConnection.get();
|
display = XDisplayConnection.get();
|
||||||
auto screen = DefaultScreen(display);
|
auto screen = DefaultScreen(display);
|
||||||
|
|
||||||
|
bool overrideRedirect = false;
|
||||||
|
if(windowType == WindowTypes.dropdownMenu || windowType == WindowTypes.popupMenu || windowType == WindowTypes.notification)// || windowType == WindowTypes.nestedChild)
|
||||||
|
overrideRedirect = true;
|
||||||
|
|
||||||
version(without_opengl) {}
|
version(without_opengl) {}
|
||||||
else {
|
else {
|
||||||
if(opengl == OpenGlOptions.yes) {
|
if(opengl == OpenGlOptions.yes) {
|
||||||
|
@ -14181,9 +14206,11 @@ mixin DynamicLoad!(XRandr, "Xrandr", 2, XRandrLibrarySuccessfullyLoaded) XRandrL
|
||||||
auto root = RootWindow(display, screen);
|
auto root = RootWindow(display, screen);
|
||||||
swa.colormap = XCreateColormap(display, root, vi.visual, AllocNone);
|
swa.colormap = XCreateColormap(display, root, vi.visual, AllocNone);
|
||||||
|
|
||||||
|
swa.override_redirect = overrideRedirect;
|
||||||
|
|
||||||
window = XCreateWindow(display, (windowType != WindowTypes.nestedChild || parent is null) ? root : parent.impl.window,
|
window = XCreateWindow(display, (windowType != WindowTypes.nestedChild || parent is null) ? root : parent.impl.window,
|
||||||
0, 0, width, height,
|
0, 0, width, height,
|
||||||
0, vi.depth, 1 /* InputOutput */, vi.visual, CWColormap, &swa);
|
0, vi.depth, 1 /* InputOutput */, vi.visual, CWColormap | CWOverrideRedirect, &swa);
|
||||||
|
|
||||||
// now try to use `glXCreateContextAttribsARB()` if it's here
|
// now try to use `glXCreateContextAttribsARB()` if it's here
|
||||||
if (!useLegacy) {
|
if (!useLegacy) {
|
||||||
|
@ -14219,10 +14246,6 @@ mixin DynamicLoad!(XRandr, "Xrandr", 2, XRandrLibrarySuccessfullyLoaded) XRandrL
|
||||||
|
|
||||||
if(opengl == OpenGlOptions.no) {
|
if(opengl == OpenGlOptions.no) {
|
||||||
|
|
||||||
bool overrideRedirect = false;
|
|
||||||
if(windowType == WindowTypes.dropdownMenu || windowType == WindowTypes.popupMenu || windowType == WindowTypes.notification)
|
|
||||||
overrideRedirect = true;
|
|
||||||
|
|
||||||
XSetWindowAttributes swa;
|
XSetWindowAttributes swa;
|
||||||
swa.background_pixel = WhitePixel(display, screen);
|
swa.background_pixel = WhitePixel(display, screen);
|
||||||
swa.border_pixel = BlackPixel(display, screen);
|
swa.border_pixel = BlackPixel(display, screen);
|
||||||
|
@ -14281,6 +14304,10 @@ mixin DynamicLoad!(XRandr, "Xrandr", 2, XRandrLibrarySuccessfullyLoaded) XRandrL
|
||||||
//{ import core.stdc.stdio; printf("winclass: [%s]\n", sdpyWindowClassStr); }
|
//{ import core.stdc.stdio; printf("winclass: [%s]\n", sdpyWindowClassStr); }
|
||||||
XClassHint klass;
|
XClassHint klass;
|
||||||
XWMHints wh;
|
XWMHints wh;
|
||||||
|
if(this.customizationFlags & WindowFlags.managesChildWindowFocus) {
|
||||||
|
wh.input = true;
|
||||||
|
wh.flags |= InputHint;
|
||||||
|
}
|
||||||
XSizeHints size;
|
XSizeHints size;
|
||||||
klass.res_name = sdpyWindowClassStr;
|
klass.res_name = sdpyWindowClassStr;
|
||||||
klass.res_class = sdpyWindowClassStr;
|
klass.res_class = sdpyWindowClassStr;
|
||||||
|
@ -14293,10 +14320,15 @@ mixin DynamicLoad!(XRandr, "Xrandr", 2, XRandrLibrarySuccessfullyLoaded) XRandrL
|
||||||
|
|
||||||
// This gives our window a close button
|
// This gives our window a close button
|
||||||
if (windowType != WindowTypes.eventOnly) {
|
if (windowType != WindowTypes.eventOnly) {
|
||||||
// FIXME: actually implement the WM_TAKE_FOCUS correctly
|
Atom[2] atoms = [GetAtom!"WM_DELETE_WINDOW"(display), GetAtom!"WM_TAKE_FOCUS"(display)];
|
||||||
//Atom[2] atoms = [GetAtom!"WM_DELETE_WINDOW"(display), GetAtom!"WM_TAKE_FOCUS"(display)];
|
int useAtoms;
|
||||||
Atom[1] atoms = [GetAtom!"WM_DELETE_WINDOW"(display)];
|
if(this.customizationFlags & WindowFlags.managesChildWindowFocus) {
|
||||||
XSetWMProtocols(display, window, atoms.ptr, cast(int) atoms.length);
|
useAtoms = 2;
|
||||||
|
} else {
|
||||||
|
useAtoms = 1;
|
||||||
|
}
|
||||||
|
assert(useAtoms <= atoms.length);
|
||||||
|
XSetWMProtocols(display, window, atoms.ptr, useAtoms);
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: windowType and customizationFlags
|
// FIXME: windowType and customizationFlags
|
||||||
|
@ -14862,21 +14894,64 @@ version(X11) {
|
||||||
break;
|
break;
|
||||||
case EventType.FocusIn:
|
case EventType.FocusIn:
|
||||||
case EventType.FocusOut:
|
case EventType.FocusOut:
|
||||||
|
|
||||||
if(auto win = e.xfocus.window in SimpleWindow.nativeMapping) {
|
if(auto win = e.xfocus.window in SimpleWindow.nativeMapping) {
|
||||||
|
/+
|
||||||
|
|
||||||
|
void info(string detail) {
|
||||||
|
string s;
|
||||||
|
import std.conv;
|
||||||
|
import std.datetime;
|
||||||
|
s ~= to!string(Clock.currTime);
|
||||||
|
s ~= " ";
|
||||||
|
s ~= e.type == EventType.FocusIn ? "in " : "out";
|
||||||
|
s ~= " ";
|
||||||
|
s ~= win.windowType == WindowTypes.nestedChild ? "child " : "main ";
|
||||||
|
s ~= e.xfocus.mode == NotifyModes.NotifyNormal ? " normal ": " grabbed ";
|
||||||
|
s ~= detail;
|
||||||
|
s ~= " ";
|
||||||
|
|
||||||
|
sdpyPrintDebugString(s);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(e.xfocus.detail) {
|
||||||
|
case NotifyDetail.NotifyAncestor: info("Ancestor"); break;
|
||||||
|
case NotifyDetail.NotifyVirtual: info("Virtual"); break;
|
||||||
|
case NotifyDetail.NotifyInferior: info("Inferior"); break;
|
||||||
|
case NotifyDetail.NotifyNonlinear: info("Nonlinear"); break;
|
||||||
|
case NotifyDetail.NotifyNonlinearVirtual: info("nlinearvirtual"); break;
|
||||||
|
case NotifyDetail.NotifyPointer: info("pointer"); break;
|
||||||
|
case NotifyDetail.NotifyPointerRoot: info("pointerroot"); break;
|
||||||
|
case NotifyDetail.NotifyDetailNone: info("none"); break;
|
||||||
|
default:
|
||||||
|
|
||||||
|
}
|
||||||
|
+/
|
||||||
|
|
||||||
|
|
||||||
if (win.xic !is null) {
|
if (win.xic !is null) {
|
||||||
//{ import core.stdc.stdio : printf; printf("XIC focus change!\n"); }
|
//{ import core.stdc.stdio : printf; printf("XIC focus change!\n"); }
|
||||||
if (e.type == EventType.FocusIn) XSetICFocus(win.xic); else XUnsetICFocus(win.xic);
|
if (e.type == EventType.FocusIn) XSetICFocus(win.xic); else XUnsetICFocus(win.xic);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(e.xfocus.detail == NotifyDetail.NotifyPointer)
|
||||||
|
break; // just ignore these they seem irrelevant
|
||||||
|
|
||||||
|
auto old = win._focused;
|
||||||
win._focused = e.type == EventType.FocusIn;
|
win._focused = e.type == EventType.FocusIn;
|
||||||
|
|
||||||
|
// yes, we are losing the focus, but to our own child. that's actually kinda keeping it.
|
||||||
|
if(e.type == EventType.FocusOut && e.xfocus.detail == NotifyDetail.NotifyInferior)
|
||||||
|
win._focused = true;
|
||||||
|
|
||||||
if(win.demandingAttention)
|
if(win.demandingAttention)
|
||||||
demandAttention(*win, false);
|
demandAttention(*win, false);
|
||||||
|
|
||||||
if(win.onFocusChange) {
|
if(old != win._focused && win.onFocusChange) {
|
||||||
XUnlockDisplay(display);
|
XUnlockDisplay(display);
|
||||||
scope(exit) XLockDisplay(display);
|
scope(exit) XLockDisplay(display);
|
||||||
win.onFocusChange(e.type == EventType.FocusIn);
|
win.onFocusChange(win._focused);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -14915,14 +14990,25 @@ version(X11) {
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if(e.xclient.data.l[0] == GetAtom!"WM_TAKE_FOCUS"(e.xany.display)) {
|
} else if(e.xclient.data.l[0] == GetAtom!"WM_TAKE_FOCUS"(e.xany.display)) {
|
||||||
import std.stdio; writeln("HAPPENED");
|
//import std.stdio; writeln("HAPPENED");
|
||||||
// user clicked the close button on the window manager
|
// user clicked the close button on the window manager
|
||||||
if(auto win = e.xclient.window in SimpleWindow.nativeMapping) {
|
if(auto win = e.xclient.window in SimpleWindow.nativeMapping) {
|
||||||
XUnlockDisplay(display);
|
XUnlockDisplay(display);
|
||||||
scope(exit) XLockDisplay(display);
|
scope(exit) XLockDisplay(display);
|
||||||
|
|
||||||
|
auto setTo = *win;
|
||||||
|
|
||||||
|
if(win.setRequestedInputFocus !is null) {
|
||||||
|
auto s = win.setRequestedInputFocus();
|
||||||
|
if(s !is null)
|
||||||
|
setTo = s;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(setTo !is null);
|
||||||
|
|
||||||
// FIXME: so this is actually supposed to focus to a relevant child window if appropriate
|
// FIXME: so this is actually supposed to focus to a relevant child window if appropriate
|
||||||
XSetInputFocus(display, e.xclient.window, RevertToParent, e.xclient.data.l[1]);
|
|
||||||
|
XSetInputFocus(display, setTo.impl.window, RevertToParent, e.xclient.data.l[1]);
|
||||||
}
|
}
|
||||||
} else if(e.xclient.message_type == GetAtom!"MANAGER"(e.xany.display)) {
|
} else if(e.xclient.message_type == GetAtom!"MANAGER"(e.xany.display)) {
|
||||||
foreach(nai; NotificationAreaIcon.activeIcons)
|
foreach(nai; NotificationAreaIcon.activeIcons)
|
||||||
|
@ -21589,9 +21675,11 @@ void guiAbortProcess(string msg) {
|
||||||
WCharzBuffer t = WCharzBuffer(msg);
|
WCharzBuffer t = WCharzBuffer(msg);
|
||||||
MessageBoxW(null, t.ptr, "Program Termination"w.ptr, 0);
|
MessageBoxW(null, t.ptr, "Program Termination"w.ptr, 0);
|
||||||
} else {
|
} else {
|
||||||
import std.stdio;
|
import core.stdc.stdio;
|
||||||
stderr.writeln(msg);
|
fwrite(msg.ptr, 1, msg.length, stderr);
|
||||||
stderr.flush();
|
msg = "\n";
|
||||||
|
fwrite(msg.ptr, 1, msg.length, stderr);
|
||||||
|
fflush(stderr);
|
||||||
}
|
}
|
||||||
|
|
||||||
abort();
|
abort();
|
||||||
|
|
Loading…
Reference in New Issue