mirror of https://github.com/adamdruppe/arsd.git
ketmar patches
This commit is contained in:
parent
b2f1022ad8
commit
02e509c394
241
simpledisplay.d
241
simpledisplay.d
|
@ -681,6 +681,7 @@ enum WindowTypes : int {
|
|||
private __gshared ushort sdpyOpenGLContextVersion = 0; // default: use legacy call
|
||||
private __gshared bool sdpyOpenGLContextCompatible = true; // default: allow "deprecated" features
|
||||
private __gshared char* sdpyWindowClassStr = null;
|
||||
private __gshared bool sdpyOpenGLContextAllowFallback = false;
|
||||
|
||||
/**
|
||||
Set OpenGL context version to use. This has no effect on non-OpenGL windows.
|
||||
|
@ -706,6 +707,18 @@ void setOpenGLContextVersion() (ubyte hi, ubyte lo) { sdpyOpenGLContextVersion =
|
|||
*/
|
||||
@property void openGLContextCompatible() (bool v) { sdpyOpenGLContextCompatible = v; }
|
||||
|
||||
/**
|
||||
Set to `true` to allow creating OpenGL context with lower version than requested
|
||||
instead of throwing. If fallback was activated (or legacy OpenGL was requested),
|
||||
`openGLContextFallbackActivated()` will return `true`.
|
||||
*/
|
||||
@property void openGLContextAllowFallback() (bool v) { sdpyOpenGLContextAllowFallback = v; }
|
||||
|
||||
/**
|
||||
After creating OpenGL window, you can check this to see if you got only "legacy" OpenGL context.
|
||||
*/
|
||||
@property bool openGLContextFallbackActivated() (bool v) { return (sdpyOpenGLContextVersion == 0); }
|
||||
|
||||
|
||||
/**
|
||||
Set window class name for all following `new SimpleWindow()` calls.
|
||||
|
@ -914,6 +927,21 @@ class SimpleWindow : CapableOfHandlingNativeEvent {
|
|||
if (!_closed) impl.showCursor();
|
||||
}
|
||||
|
||||
/** "Warp" mouse pointer to coordinates relative to window top-left corner. Return "success" flag.
|
||||
*
|
||||
* Currently only supported on X11, so Windows implementation will return `false`.
|
||||
*
|
||||
* Note: "warping" pointer will not send any synthesised mouse events, so you probably doesn't want
|
||||
* to use it to move mouse pointer to some active GUI area, for example, as your window won't
|
||||
* receive "mouse moved here" event.
|
||||
*/
|
||||
bool warpMouse (int x, int y) {
|
||||
version(X11) {
|
||||
if (!_closed) { impl.warpMouse(x, y); return true; }
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Set window minimal size.
|
||||
void setMinSize (int minwidth, int minheight) {
|
||||
if (!_closed) impl.setMinSize(minwidth, minheight);
|
||||
|
@ -2061,6 +2089,132 @@ struct KeyEvent {
|
|||
uint modifierState; /// see enum [ModifierState]. They are bitwise combined together.
|
||||
|
||||
SimpleWindow window; /// associated Window
|
||||
|
||||
// convert key event to simplified string representation a-la emacs
|
||||
const(char)[] toStrBuf(bool growdest=false) (char[] dest) const nothrow @trusted {
|
||||
uint dpos = 0;
|
||||
void put (const(char)[] s...) nothrow @trusted {
|
||||
static if (growdest) {
|
||||
foreach (char ch; s) if (dpos < dest.length) dest.ptr[dpos++] = ch; else { dest ~= ch; ++dpos; }
|
||||
} else {
|
||||
foreach (char ch; s) if (dpos < dest.length) dest.ptr[dpos++] = ch;
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.key) return null;
|
||||
|
||||
// put modifiers
|
||||
if (this.modifierState&ModifierState.ctrl) put("C-");
|
||||
if (this.modifierState&ModifierState.alt) put("M-");
|
||||
if (this.modifierState&ModifierState.shift) put("S-");
|
||||
|
||||
foreach (string kn; __traits(allMembers, Key)) {
|
||||
if (this.key == __traits(getMember, Key, kn)) {
|
||||
put(kn);
|
||||
return dest[0..dpos];
|
||||
}
|
||||
}
|
||||
|
||||
put("Unknown");
|
||||
return dest[0..dpos];
|
||||
}
|
||||
|
||||
string toStr() () { return cast(string)toStrBuf!true(null); } // it is safe to cast here
|
||||
|
||||
// sorry for pasta, but i don't want to create new struct in `opEquals()`
|
||||
static KeyEvent parse (const(char)[] name) nothrow @trusted @nogc {
|
||||
KeyEvent res;
|
||||
while (name.length && name.ptr[0] <= ' ') name = name[1..$];
|
||||
while (name.length && name[$-1] <= ' ') name = name[0..$-1];
|
||||
uint mods = 0;
|
||||
while (name.length > 1 && name.ptr[1] == '-') {
|
||||
switch (name.ptr[0]) {
|
||||
case 'C': case 'c': mods |= ModifierState.ctrl; break;
|
||||
case 'M': case 'm': mods |= ModifierState.alt; break;
|
||||
case 'S': case 's': mods |= ModifierState.shift; break;
|
||||
default: return res; // alas
|
||||
}
|
||||
name = name[2..$];
|
||||
}
|
||||
if (name.length == 0) return res;
|
||||
res.modifierState = mods;
|
||||
//HACK
|
||||
if (name.length == 1 && name.ptr[0] >= '0' && name.ptr[0] <= '9') {
|
||||
final switch (name.ptr[0]) {
|
||||
case '0': name = "N0"; break;
|
||||
case '1': name = "N1"; break;
|
||||
case '2': name = "N2"; break;
|
||||
case '3': name = "N3"; break;
|
||||
case '4': name = "N4"; break;
|
||||
case '5': name = "N5"; break;
|
||||
case '6': name = "N6"; break;
|
||||
case '7': name = "N7"; break;
|
||||
case '8': name = "N8"; break;
|
||||
case '9': name = "N9"; break;
|
||||
}
|
||||
}
|
||||
foreach (string kn; __traits(allMembers, Key)) {
|
||||
if (kn.length == name.length) {
|
||||
// case-insensitive comapre
|
||||
bool ok = true;
|
||||
foreach (immutable ci, char c0; kn) {
|
||||
if (c0 >= 'A' && c0 <= 'Z') c0 += 32; // poor man's tolower
|
||||
char c1 = name.ptr[ci];
|
||||
if (c1 >= 'A' && c1 <= 'Z') c1 += 32; // poor man's tolower
|
||||
if (c0 != c1) { ok = false; break; }
|
||||
}
|
||||
if (ok) { res.key = __traits(getMember, Key, kn); return res; }
|
||||
}
|
||||
}
|
||||
return res; // at least modifier state, lol
|
||||
}
|
||||
|
||||
bool opEquals() (const(char)[] name) const nothrow @trusted @nogc {
|
||||
while (name.length && name.ptr[0] <= ' ') name = name[1..$];
|
||||
while (name.length && name[$-1] <= ' ') name = name[0..$-1];
|
||||
if (!this.key) return (name.length == 0);
|
||||
uint mods = 0;
|
||||
while (name.length > 1 && name.ptr[1] == '-') {
|
||||
switch (name.ptr[0]) {
|
||||
case 'C': case 'c': mods |= ModifierState.ctrl; break;
|
||||
case 'M': case 'm': mods |= ModifierState.alt; break;
|
||||
case 'S': case 's': mods |= ModifierState.shift; break;
|
||||
default: return false; // alas
|
||||
}
|
||||
name = name[2..$];
|
||||
}
|
||||
if (name.length == 0) return false;
|
||||
if ((this.modifierState&(ModifierState.ctrl|ModifierState.alt|ModifierState.shift)) != mods) return false;
|
||||
//HACK
|
||||
if (name.length == 1 && name.ptr[0] >= '0' && name.ptr[0] <= '9') {
|
||||
final switch (name.ptr[0]) {
|
||||
case '0': name = "N0"; break;
|
||||
case '1': name = "N1"; break;
|
||||
case '2': name = "N2"; break;
|
||||
case '3': name = "N3"; break;
|
||||
case '4': name = "N4"; break;
|
||||
case '5': name = "N5"; break;
|
||||
case '6': name = "N6"; break;
|
||||
case '7': name = "N7"; break;
|
||||
case '8': name = "N8"; break;
|
||||
case '9': name = "N9"; break;
|
||||
}
|
||||
}
|
||||
foreach (string kn; __traits(allMembers, Key)) {
|
||||
if (kn.length == name.length) {
|
||||
// case-insensitive comapre
|
||||
bool ok = true;
|
||||
foreach (immutable ci, char c0; kn) {
|
||||
if (c0 >= 'A' && c0 <= 'Z') c0 += 32; // poor man's tolower
|
||||
char c1 = name.ptr[ci];
|
||||
if (c1 >= 'A' && c1 <= 'Z') c1 += 32; // poor man's tolower
|
||||
if (c0 != c1) { ok = false; break; }
|
||||
}
|
||||
if (ok && this.key == __traits(getMember, Key, kn)) return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// Type of a [MouseEvent]
|
||||
|
@ -3859,11 +4013,19 @@ version(Windows) {
|
|||
0/*None*/,
|
||||
];
|
||||
ghRC = wglCreateContextAttribsARB(ghDC, null, contextAttribs.ptr);
|
||||
if (ghRC is null && sdpyOpenGLContextAllowFallback) {
|
||||
// activate fallback mode
|
||||
sdpyOpenGLContextVersion = 0;
|
||||
ghRC = wglCreateContext(ghDC);
|
||||
}
|
||||
if (ghRC is null)
|
||||
throw new Exception("wglCreateContextAttribsARB");
|
||||
} else {
|
||||
// try to do at least something
|
||||
ghRC = wglCreateContext(ghDC);
|
||||
if (sdpyOpenGLContextAllowFallback || sdpyOpenGLContextVersion == 0) {
|
||||
sdpyOpenGLContextVersion = 0;
|
||||
ghRC = wglCreateContext(ghDC);
|
||||
}
|
||||
if (ghRC is null)
|
||||
throw new Exception("wglCreateContext");
|
||||
}
|
||||
|
@ -4951,6 +5113,7 @@ version(X11) {
|
|||
XIC xic; // input context
|
||||
int curHidden = 0; // counter
|
||||
Cursor blankCurPtr = 0;
|
||||
int warpEventCount = 0; // number of mouse movement events to eat
|
||||
|
||||
void delegate(XEvent) setSelectionHandler;
|
||||
void delegate(in char[]) getSelectionHandler;
|
||||
|
@ -5001,6 +5164,29 @@ version(X11) {
|
|||
if (--curHidden == 0) XUndefineCursor(display, window);
|
||||
}
|
||||
|
||||
void warpMouse (int x, int y) {
|
||||
// here i will send dummy "ignore next mouse motion" event,
|
||||
// 'cause `XWarpPointer()` sends synthesised mouse motion,
|
||||
// and we don't need to report it to the user (as warping is
|
||||
// used when the user needs movement deltas).
|
||||
//XClientMessageEvent xclient;
|
||||
XEvent e;
|
||||
e.xclient.type = EventType.ClientMessage;
|
||||
e.xclient.window = window;
|
||||
e.xclient.message_type = GetAtom!("_X11SDPY_INSMME_FLAG_EVENT_", true)(display); // let's hope nobody else will use such stupid name ;-)
|
||||
e.xclient.format = 32;
|
||||
e.xclient.data.l[0] = 0;
|
||||
debug(x11sdpy_warp_debug) { import core.stdc.stdio : printf; printf("X11: sending \"INSMME\"...\n"); }
|
||||
//{ import core.stdc.stdio : printf; printf("*X11 CLIENT: w=%u; type=%u; [0]=%u\n", cast(uint)e.xclient.window, cast(uint)e.xclient.message_type, cast(uint)e.xclient.data.l[0]); }
|
||||
XSendEvent(display, window, false, EventMask.NoEventMask, /*cast(XEvent*)&xclient*/&e);
|
||||
// now warp pointer...
|
||||
debug(x11sdpy_warp_debug) { import core.stdc.stdio : printf; printf("X11: sending \"warp\"...\n"); }
|
||||
XWarpPointer(display, None, window, 0, 0, 0, 0, x, y);
|
||||
// ...and flush
|
||||
debug(x11sdpy_warp_debug) { import core.stdc.stdio : printf; printf("X11: flushing...\n"); }
|
||||
XFlush(display);
|
||||
}
|
||||
|
||||
void setTitle(string title) {
|
||||
if (title.ptr is null) title = "";
|
||||
auto XA_UTF8 = XInternAtom(display, "UTF8_STRING".ptr, false);
|
||||
|
@ -5127,11 +5313,17 @@ version(X11) {
|
|||
0/*None*/,
|
||||
];
|
||||
glc = glXCreateContextAttribsARB(display, fbconf, null, 1/*True*/, contextAttribs.ptr);
|
||||
if (glc is null && sdpyOpenGLContextAllowFallback) {
|
||||
sdpyOpenGLContextVersion = 0;
|
||||
glc = glXCreateContext(display, vi, null, /*GL_TRUE*/1);
|
||||
}
|
||||
//{ import core.stdc.stdio; printf("using modern ogl v%d.%d\n", contextAttribs[1], contextAttribs[3]); }
|
||||
} else {
|
||||
// fallback to old GLX call
|
||||
useglxlegacycontext:
|
||||
glc = glXCreateContext(display, vi, null, /*GL_TRUE*/1);
|
||||
if (sdpyOpenGLContextAllowFallback || sdpyOpenGLContextVersion == 0) {
|
||||
sdpyOpenGLContextVersion = 0;
|
||||
glc = glXCreateContext(display, vi, null, /*GL_TRUE*/1);
|
||||
}
|
||||
}
|
||||
// sync to ensure any errors generated are processed
|
||||
XSync(display, 0/*False*/);
|
||||
|
@ -5702,15 +5894,23 @@ version(X11) {
|
|||
}
|
||||
break;
|
||||
case EventType.ClientMessage:
|
||||
if(e.xclient.data.l[0] == GetAtom!"WM_DELETE_WINDOW"(e.xany.display)) {
|
||||
// user clicked the close button on the window manager
|
||||
// FIXME: not implemented on Windows
|
||||
if(auto win = e.xclient.window in SimpleWindow.nativeMapping) {
|
||||
XUnlockDisplay(display);
|
||||
scope(exit) XLockDisplay(display);
|
||||
if ((*win).closeQuery !is null) (*win).closeQuery(); else (*win).close();
|
||||
if (e.xclient.message_type == GetAtom!("_X11SDPY_INSMME_FLAG_EVENT_", true)(e.xany.display)) {
|
||||
// "ignore next mouse motion" event, increment ignore counter for teh window
|
||||
if (auto win = e.xclient.window in SimpleWindow.nativeMapping) {
|
||||
++(*win).warpEventCount;
|
||||
debug(x11sdpy_warp_debug) { import core.stdc.stdio : printf; printf("X11: got \"INSMME\" message, new count=%d\n", (*win).warpEventCount); }
|
||||
} else {
|
||||
debug(x11sdpy_warp_debug) { import core.stdc.stdio : printf; printf("X11: got \"INSMME\" WTF?!!\n"); }
|
||||
}
|
||||
} else if(e.xclient.data.l[0] == GetAtom!"WM_DELETE_WINDOW"(e.xany.display)) {
|
||||
// user clicked the close button on the window manager
|
||||
// FIXME: not implemented on Windows
|
||||
if(auto win = e.xclient.window in SimpleWindow.nativeMapping) {
|
||||
XUnlockDisplay(display);
|
||||
scope(exit) XLockDisplay(display);
|
||||
if ((*win).closeQuery !is null) (*win).closeQuery(); else (*win).close();
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case EventType.MapNotify:
|
||||
if(auto win = e.xmap.window in SimpleWindow.nativeMapping) {
|
||||
|
@ -5771,11 +5971,18 @@ version(X11) {
|
|||
mouse.modifierState = event.state;
|
||||
|
||||
if(auto win = e.xmotion.window in SimpleWindow.nativeMapping) {
|
||||
(*win).mdx(mouse);
|
||||
if((*win).handleMouseEvent) {
|
||||
XUnlockDisplay(display);
|
||||
scope(exit) XLockDisplay(display);
|
||||
(*win).handleMouseEvent(mouse);
|
||||
if (win.warpEventCount > 0) {
|
||||
debug(x11sdpy_warp_debug) { import core.stdc.stdio : printf; printf("X11: got \"warp motion\" message, current count=%d\n", (*win).warpEventCount); }
|
||||
--(*win).warpEventCount;
|
||||
(*win).mdx(mouse); // so deltas will be correctly updated
|
||||
} else {
|
||||
win.warpEventCount = 0; // just in case
|
||||
(*win).mdx(mouse);
|
||||
if((*win).handleMouseEvent) {
|
||||
XUnlockDisplay(display);
|
||||
scope(exit) XLockDisplay(display);
|
||||
(*win).handleMouseEvent(mouse);
|
||||
}
|
||||
}
|
||||
mouse.window = *win;
|
||||
}
|
||||
|
@ -7448,6 +7655,8 @@ struct Visual
|
|||
int XLowerWindow(Display*, Window);
|
||||
int XRaiseWindow(Display*, Window);
|
||||
|
||||
int XWarpPointer(Display *display, Window src_w, Window dest_w, int src_x, int src_y, uint src_width, uint src_height, int dest_x, int dest_y);
|
||||
|
||||
int XGetInputFocus(Display*, Window*, int*);
|
||||
int XSetInputFocus(Display*, Window, int, Time);
|
||||
alias XErrorHandler = int function(Display*, XErrorEvent*);
|
||||
|
|
Loading…
Reference in New Issue