XWorkstation.st
changeset 6567 803364ec1f7d
parent 6551 eff1347ac76f
child 6569 844102405f89
--- a/XWorkstation.st	Thu Aug 28 17:14:38 2014 +0200
+++ b/XWorkstation.st	Sat Sep 20 15:30:53 2014 +0200
@@ -27,7 +27,7 @@
 		selectionFetchers selectionHandlers preWaitAction xlibTimeout
 		xlibTimeoutForWindowCreation hasConnectionBroken uniqueDeviceID
 		stxDeviceAtom uuidAtom primaryBuffer windowGroupWindow
-		maxOperationsUntilFlush operationsUntilFlush'
+		maxOperationsUntilFlush operationsUntilFlush lastError'
 	classVariableNames:'RawKeySymTranslation ConservativeSync MaxStringLength
 		DefaultXLibTimeout DefaultXLibTimeoutForWindowCreation
 		ErrorDBCache'
@@ -564,6 +564,9 @@
 !XWorkstation class methodsFor:'initialization'!
 
 initialize
+    "/ ConservativeSync is required for some Xlib implementation,
+    "/ where eventPending returns wrong if we do not flush the buffer.
+    "/ (especially Win32 & Xlib)
     ConservativeSync := OperatingSystem isMSWINDOWSlike.
 
     "/ some XServers crash, when given too long strings in XDrawString/XDrawInageString.
@@ -4998,10 +5001,6 @@
     "forward an expose event for some view"
 
     self exposeX:x y:y width:w height:h view:view.
-
-
-
-
 !
 
 focusIn:view mode:mode detail:detail
@@ -5026,10 +5025,6 @@
     "forward a graphics-expose event for some view"
 
     self graphicsExposeX:x y:y width:w height:h final:(count==0) view:view
-
-
-
-
 !
 
 keyPress:view key:key code:keyCode state:state x:x y:y rootX:rX rootY:rY time:time
@@ -5053,6 +5048,7 @@
     "very low-level mapping of X11 event symbols to common ST/X event symbols"
     commonKey := rawKeySymTranslation at:key ifAbsent:key.
 
+view errorPrint. ' key:' errorPrint. commonKey errorPrintCR.
     self keyPress:commonKey x:x y:y view:view.
 !
 
@@ -5352,6 +5348,10 @@
 !
 
 dispatchEvent:evArray
+    "a raw event array as coming from the low level C code is converted
+     to a message send here.
+     Also, the windowID from the event array is mapped to a view object."
+
     |viewId view evType arguments|
 
     viewId := evArray at:1.
@@ -5372,9 +5372,10 @@
 	self perform:evType withArguments:arguments.
 	^ true.
     ].
-'********** unhandled event:' errorPrintCR.
-evType errorPrintCR. (evArray at:2) errorPrintCR.
-'********** see dispatchEvent' errorPrintCR.
+
+    '********** unhandled event:' errorPrintCR.
+    evType errorPrintCR. (evArray at:2) errorPrintCR.
+    '********** see dispatchEvent' errorPrintCR.
     ^ false
 !
 
@@ -5681,14 +5682,14 @@
 !
 
 getEventFor:aViewIdOrNil withMask:eventMask into:anEventArray
-    "read next event if there is one and put events data into anEventArray.
+    "read next event if there is one and put event's data into anEventArray.
      If aViewIdOrNil is nil, events for any view are fetched;
      otherwise only events for that specific view will be fetched.
      Returns true, if there was an event, false otherwise.
      This method may block - so you better check for pending events
      before calling for it.
 
-     The event fields are placed them into anEventArray (must be at least size 13):
+     The event fields are placed into anEventArray (must be at least size 13):
      the fields are:
 	1:      windowID
 	2:      eventType-ID
@@ -6421,107 +6422,112 @@
     int state;
 
     if (__isSmallInteger(stateMask)) {
-        state = __intVal(stateMask);
+	state = __intVal(stateMask);
     } else {
-        state = 0;
+	state = 0;
     }
 
     if (ISCONNECTED
      && __isSmallInteger(xPos) && __isSmallInteger(yPos)
      && (__isSmallInteger(keySymCodeOrButtonNr) || __isStringLike(keySymCodeOrButtonNr))
      && (__isExternalAddress(targetId) || __isInteger(targetId))) {
-        Display *dpy = myDpy;
-
-        XEvent ev;
-        Window target;
-        Status result;
-        KeySym keySym, *syms;
-        int screen = __intVal(__INST(screen));
-        char s[2];
-        int nSyms;
-
-        if ((typeSymbol == @symbol(keyPress))
-         || (typeSymbol == @symbol(keyRelease))) {
-            if (__isStringLike(keySymCodeOrButtonNr)) {
-                keySym = XStringToKeysym(__stringVal(keySymCodeOrButtonNr));
-            } else {
-                if (__isCharacter(keySymCodeOrButtonNr)) {
-                    s[0] = __intVal(__characterVal(keySymCodeOrButtonNr));
-                    s[1] = '\0';
-                    keySym = XStringToKeysym(s);
-                } else {
-                    keySym = (KeySym) __intVal(keySymCodeOrButtonNr);
-                }
-            }
-            ev.xkey.keycode = XKeysymToKeycode(dpy, keySym);
-
-            if (stateMask == nil) {
-                /*
-                 * get the modifier from the keySym
-                 */
-                nSyms = 0;
-                syms = XGetKeyboardMapping(dpy, ev.xkey.keycode, 1, &nSyms);
-                if (syms) {
-                    int i;
-
-                    for (i=0; i<nSyms; i++) {
-                        if (syms[i] == keySym) {
+	Display *dpy = myDpy;
+
+	XEvent ev;
+	Window target;
+	Status result;
+	KeySym keySym, *syms;
+	int screen = __intVal(__INST(screen));
+	int nSyms;
+
+	if ((typeSymbol == @symbol(keyPress))
+	 || (typeSymbol == @symbol(keyRelease))) {
+	    if (__isStringLike(keySymCodeOrButtonNr)) {
+		keySym = XStringToKeysym(__stringVal(keySymCodeOrButtonNr));
+	    } else {
+		if (__isCharacter(keySymCodeOrButtonNr)) {
+		    char s[2];
+		    s[0] = __intVal(__characterVal(keySymCodeOrButtonNr));
+		    s[1] = '\0';
+		    keySym = XStringToKeysym(s);
+		} else {
+		    if (__isSmallInteger(keySymCodeOrButtonNr)) {
+			keySym = (KeySym) __intVal(keySymCodeOrButtonNr);
+		    } else {
+			goto notOk;
+		    }
+		}
+	    }
+	    ev.xkey.keycode = XKeysymToKeycode(dpy, keySym);
+
+	    if (stateMask == nil) {
+		/*
+		 * get the modifier from the keySym
+		 */
+		nSyms = 0;
+		syms = XGetKeyboardMapping(dpy, ev.xkey.keycode, 1, &nSyms);
+		if (syms) {
+		    int i;
+
+		    for (i=0; i<nSyms; i++) {
+			if (syms[i] == keySym) {
 #ifdef MODIFIERDEBUG
-                            console_printf("modifier-index is %d\n", i);
-#endif
-                            if (i) state = (1 << (i-1));
-                            break;
-                        }
-                    }
-                    XFree(syms);
-                }
-            }
-        } else {
-            if ((typeSymbol == @symbol(buttonPress))
-             || (typeSymbol == @symbol(buttonRelease))) {
-                if (__isSmallInteger(keySymCodeOrButtonNr)) {
-                    ev.xbutton.button = __intVal(keySymCodeOrButtonNr);
-                } else {
-                    ev.xbutton.button = 1;
-                }
-            } else {
-                DPRINTF(("invalid sendEvent typeSymbol\n"));
-                RETURN (false);
-            }
-        }
-
-        if (typeSymbol == @symbol(keyPress))
-            ev.xany.type = KeyPress;
-        else if (typeSymbol == @symbol(keyRelease))
-            ev.xany.type = KeyRelease;
-        else if (typeSymbol == @symbol(buttonPress))
-            ev.xany.type = ButtonPress;
-        else if (typeSymbol == @symbol(buttonRelease))
-            ev.xany.type = ButtonRelease;
-
-        if (__isExternalAddress(targetId)) {
-            target = __WindowVal(targetId);
-        } else {
-            target = (Window) __longIntVal(targetId);
-        }
-        ev.xkey.window = target;
-        ev.xkey.same_screen = 1;
-        ev.xkey.subwindow = 0;
-        ev.xkey.root = RootWindow(dpy, screen);
-        ev.xkey.x = __intVal(xPos);
-        ev.xkey.y = __intVal(yPos);
-        ev.xkey.state = state;
-        ev.xkey.time = CurrentTime;
-
-        ENTER_XLIB();
-        result = XSendEvent(dpy, target, False, 0 , &ev);
-        LEAVE_XLIB();
-        if ((result == BadValue) || (result == BadWindow)) {
-            DPRINTF(("bad status\n"));
-            RETURN ( false )
-        }
-        RETURN (true)
-    }
+			    console_printf("modifier-index is %d\n", i);
+#endif
+			    if (i) state = (1 << (i-1));
+			    break;
+			}
+		    }
+		    XFree(syms);
+		}
+	    }
+	} else {
+	    if ((typeSymbol == @symbol(buttonPress))
+	     || (typeSymbol == @symbol(buttonRelease))) {
+		if (__isSmallInteger(keySymCodeOrButtonNr)) {
+		    ev.xbutton.button = __intVal(keySymCodeOrButtonNr);
+		} else {
+		    ev.xbutton.button = 1;
+		}
+	    } else {
+		DPRINTF(("invalid sendEvent typeSymbol\n"));
+		RETURN (false);
+	    }
+	}
+
+	if (typeSymbol == @symbol(keyPress))
+	    ev.xany.type = KeyPress;
+	else if (typeSymbol == @symbol(keyRelease))
+	    ev.xany.type = KeyRelease;
+	else if (typeSymbol == @symbol(buttonPress))
+	    ev.xany.type = ButtonPress;
+	else if (typeSymbol == @symbol(buttonRelease))
+	    ev.xany.type = ButtonRelease;
+
+	if (__isExternalAddress(targetId)) {
+	    target = __WindowVal(targetId);
+	} else {
+	    target = (Window) __longIntVal(targetId);
+	}
+	ev.xkey.window = target;
+	ev.xkey.same_screen = 1;
+	ev.xkey.subwindow = 0;
+	ev.xkey.root = RootWindow(dpy, screen);
+	ev.xkey.x = __intVal(xPos);
+	ev.xkey.y = __intVal(yPos);
+	ev.xkey.state = state;
+	ev.xkey.time = CurrentTime;
+
+	ENTER_XLIB();
+	result = XSendEvent(dpy, target, False, 0 , &ev);
+	LEAVE_XLIB();
+	if ((result == BadValue) || (result == BadWindow)) {
+	    DPRINTF(("bad status\n"));
+	    RETURN ( false )
+	}
+	RETURN (true)
+    }
+  notOk: ;
 %}.
     self primitiveFailedOrClosedConnection.
     ^ false
@@ -6535,63 +6541,63 @@
      v selectFromCharacterPosition:1 to:5.
 
      "/ CTRL-c
-     v device 
-         sendKeyOrButtonEvent:#keyPress
-         x:10 y:10
-         keyOrButton:#'Control'
-         state:(v device ctrlModifierMask)
-         toViewId: v id.
-
-     v device 
-         sendKeyOrButtonEvent:#keyPress
-         x:10 y:10
-         keyOrButton:'c'
-         state:(v device ctrlModifierMask)
-         toViewId: v id.
-
-     v device 
-         sendKeyOrButtonEvent:#keyRelease
-         x:10 y:10
-         keyOrButton:'c'
-         state:(v device ctrlModifierMask)
-         toViewId: v id.
-
-     v device 
-         sendKeyOrButtonEvent:#keyRelease
-         x:10 y:10
-         keyOrButton:#'Control'
-         state:0
-         toViewId: v id.
+     v device
+	 sendKeyOrButtonEvent:#keyPress
+	 x:10 y:10
+	 keyOrButton:#'Control'
+	 state:(v device ctrlModifierMask)
+	 toViewId: v id.
+
+     v device
+	 sendKeyOrButtonEvent:#keyPress
+	 x:10 y:10
+	 keyOrButton:'c'
+	 state:(v device ctrlModifierMask)
+	 toViewId: v id.
+
+     v device
+	 sendKeyOrButtonEvent:#keyRelease
+	 x:10 y:10
+	 keyOrButton:'c'
+	 state:(v device ctrlModifierMask)
+	 toViewId: v id.
+
+     v device
+	 sendKeyOrButtonEvent:#keyRelease
+	 x:10 y:10
+	 keyOrButton:#'Control'
+	 state:0
+	 toViewId: v id.
 
      "/ CTRL-v
 
-     v device 
-         sendKeyOrButtonEvent:#keyPress
-         x:10 y:10
-         keyOrButton:#'Control'
-         state:(v device ctrlModifierMask)
-         toViewId: v id.
-
-     v device 
-         sendKeyOrButtonEvent:#keyPress
-         x:10 y:10
-         keyOrButton:'v'
-         state:(v device ctrlModifierMask)
-         toViewId: v id.
-
-     v device 
-         sendKeyOrButtonEvent:#keyRelease
-         x:10 y:10
-         keyOrButton:'v'
-         state:(v device ctrlModifierMask)
-         toViewId: v id.
-
-     v device 
-         sendKeyOrButtonEvent:#keyRelease
-         x:10 y:10
-         keyOrButton:#'Control'
-         state:0
-         toViewId: v id.
+     v device
+	 sendKeyOrButtonEvent:#keyPress
+	 x:10 y:10
+	 keyOrButton:#'Control'
+	 state:(v device ctrlModifierMask)
+	 toViewId: v id.
+
+     v device
+	 sendKeyOrButtonEvent:#keyPress
+	 x:10 y:10
+	 keyOrButton:'v'
+	 state:(v device ctrlModifierMask)
+	 toViewId: v id.
+
+     v device
+	 sendKeyOrButtonEvent:#keyRelease
+	 x:10 y:10
+	 keyOrButton:'v'
+	 state:(v device ctrlModifierMask)
+	 toViewId: v id.
+
+     v device
+	 sendKeyOrButtonEvent:#keyRelease
+	 x:10 y:10
+	 keyOrButton:#'Control'
+	 state:0
+	 toViewId: v id.
 END"
 ! !
 
@@ -7548,23 +7554,23 @@
     |names|
 
     listOfXFonts isNil ifTrue:[
-        names := self getAvailableFontsMatching:'*'.
-        names isNil ifTrue:[
-            "no names returned ..."
-            ^ nil
-        ].
-        listOfXFonts := names collect:[:aName | self fontDescriptionFromXFontName:aName].
-        listOfXFonts := FontDescription genericFonts, listOfXFonts.
+	names := self getAvailableFontsMatching:'*'.
+	names isNil ifTrue:[
+	    "no names returned ..."
+	    ^ nil
+	].
+	listOfXFonts := names collect:[:aName | self fontDescriptionFromXFontName:aName].
+	listOfXFonts := FontDescription genericFonts, listOfXFonts.
     ].
 
     (XftFontDescription notNil
-            and:[ XftFontDescription isLoaded
-            and:[ self supportsXFTFonts ]]
+	    and:[ XftFontDescription isLoaded
+	    and:[ self supportsXFTFonts ]]
     ) ifTrue:[
-        UserPreferences current useXftFontsOnly ifTrue:[
-            ^ (XftFontDescription listOfAvailableFonts)
-        ].
-        ^ listOfXFonts , (XftFontDescription listOfAvailableFonts).
+	UserPreferences current useXftFontsOnly ifTrue:[
+	    ^ (XftFontDescription listOfAvailableFonts)
+	].
+	^ listOfXFonts , (XftFontDescription listOfAvailableFonts).
     ].
     ^ listOfXFonts
 
@@ -7860,6 +7866,196 @@
     ^ 0
 ! !
 
+!XWorkstation methodsFor:'grabbing-keys'!
+
+grabKey:keySymCodeOrChar modifier:modifierMaskOrNil window:aWindowIdOrNil
+    "grab a single key either for an individual window
+     or the whole screen (if aWindowIdOrNil is nil).
+     The keySymCodeOrChar argument may be a keySym (name of a key) or an integer (the keySymCode)
+     or a character.
+     The modifierMaskOrNil is as mask as returned by altModifierMask, ctrlModifierMask, etc.
+     if nil, the key is grabbed with AnyModifier.
+     Only the key is passed to myself - no permanent grab is installed.
+     (GrabModeAsync)"
+
+    ^ sekf
+	grabKey:keySymCodeOrChar
+	modifier:modifierMaskOrNil
+	grabModeKeyboard:#GrabModeAsync
+	grabModePointer:#GrabModeAsync
+	window:aWindowIdOrNil
+!
+
+grabKey:keySymCodeOrChar modifier:modifierMaskOrNil grabModeKeyboard:modeKbd grabModePointer:modePtr window:aWindowIdOrNil
+    "internal basic entry to grab a single key either for an individual window
+     or the whole screen (if aWindowIdOrNil is nil).
+     The keySymCodeOrChar argument may be a keySym (name of a key) or an integer (the keySymCode)
+     or a character.
+     The modifierMaskOrNil is as mask as returned by altModifierMask, ctrlModifierMask, etc.
+     if nil, the key is grabbed with AnyModifier.
+     ModeKbd and modePtr are symbols GrabModeAsync or GrabModeSync.
+
+     After that, this key-event will no longer be sent to the window/screen.
+
+     Use with care: the only useful application is to define a special hotKey
+     to start/stop event recorders without a need for a click or focus change.
+     Once grabbed, those key events will be exclusively reported to me.
+
+     Use GrabModeSync with big care - you can easily lock up your Xserver,
+     and have to kill ST/X or force an ungrab from a remote login if you have.
+    "
+
+%{
+    int modifierMask = AnyModifier;
+    KeySym keySym, *syms;
+
+    if (__isStringLike(keySymCodeOrChar)) {
+	keySym = XStringToKeysym(__stringVal(keySymCodeOrChar));
+    } else {
+	if (__isCharacter(keySymCodeOrChar)) {
+	    char s[2];
+
+	    s[0] = __intVal(__characterVal(keySymCodeOrChar));
+	    s[1] = '\0';
+	    keySym = XStringToKeysym(s);
+	} else {
+	    if (__isSmallInteger(keySymCodeOrChar)) {
+		keySym = (KeySym) __intVal(keySymCodeOrChar);
+	    } else {
+		goto notOK;
+	    }
+	}
+    }
+
+    if (modifierMaskOrNil != nil) {
+	if (__isSmallInteger(modifierMaskOrNil)) {
+	    modifierMask = __intVal(modifierMaskOrNil);
+	} else {
+	    goto notOK;
+	}
+    }
+
+    if (ISCONNECTED) {
+	Display *dpy;
+	Window window;
+	int keyCode;
+	int result;
+	int mKbd, mPtr;
+
+	mKbd = modeKbd == @symbol(GrabModeAsync) ? GrabModeAsync :GrabModeSync;
+	mPtr = modePtr == @symbol(GrabModeAsync) ? GrabModeAsync :GrabModeSync;
+
+	dpy = myDpy;
+	keyCode = XKeysymToKeycode(dpy, keySym);
+	if (__isExternalAddress(aWindowIdOrNil)) {
+	    window = __WindowVal(aWindowIdOrNil);
+	} else {
+	    int screen;
+
+	    screen = DefaultScreen(dpy);
+	    window = RootWindow(dpy, screen);
+	}
+	ENTER_XLIB();
+
+	result = XGrabKey (dpy,
+	    keyCode, modifierMask, window,
+	    False, mKbd, mPtr );
+
+	XSync(dpy, True);
+	XFlush(dpy);
+
+	LEAVE_XLIB();
+
+	if (result != Success) {
+	    if (result == BadAccess) {
+		__INST(lastError) = @symbol(badAccess);
+	    } else {
+		__INST(lastError) = @symbol(other);
+	    }
+	    RETURN (false);
+	}
+	RETURN (true);
+    }
+  notOK: ;
+%}.
+    self primitiveFailedOrClosedConnection
+!
+
+ungrabKey:keySymCodeOrChar modifier:modifierMaskOrNil window:aWindowIdOrNil
+    "ungrab a single key as previously grabbed via grabKey:
+     Read the comment there."
+
+%{
+    int modifierMask = AnyModifier;
+    KeySym keySym, *syms;
+
+    if (__isStringLike(keySymCodeOrChar)) {
+	keySym = XStringToKeysym(__stringVal(keySymCodeOrChar));
+    } else {
+	if (__isCharacter(keySymCodeOrChar)) {
+	    char s[2];
+
+	    s[0] = __intVal(__characterVal(keySymCodeOrChar));
+	    s[1] = '\0';
+	    keySym = XStringToKeysym(s);
+	} else {
+	    if (__isSmallInteger(keySymCodeOrChar)) {
+		keySym = (KeySym) __intVal(keySymCodeOrChar);
+	    } else {
+		goto notOK;
+	    }
+	}
+    }
+
+    if (modifierMaskOrNil != nil) {
+	if (__isSmallInteger(modifierMaskOrNil)) {
+	    modifierMask = __intVal(modifierMaskOrNil);
+	} else {
+	    goto notOK;
+	}
+    }
+
+    if (ISCONNECTED) {
+	Display *dpy;
+	Window window;
+	int keyCode;
+	int result;
+
+	dpy = myDpy;
+	if (__isExternalAddress(aWindowIdOrNil)) {
+	    window = __WindowVal(aWindowIdOrNil);
+	} else {
+	    int screen;
+
+	    screen = DefaultScreen(dpy);
+	    window = RootWindow(dpy, screen);
+	}
+	keyCode = XKeysymToKeycode(dpy, keySym);
+
+	ENTER_XLIB();
+
+	result = XUngrabKey (dpy, keyCode, modifierMask, window);
+
+	XSync(dpy, True);
+	XFlush(dpy);
+
+	LEAVE_XLIB();
+
+	if (result != Success) {
+	    if (result == BadAccess) {
+		__INST(lastError) = @symbol(badAccess);
+	    } else {
+		__INST(lastError) = @symbol(other);
+	    }
+	    RETURN (false);
+	}
+	RETURN (true);
+    }
+  notOK: ;
+%}.
+    self primitiveFailedOrClosedConnection
+! !
+
 !XWorkstation methodsFor:'grabbing'!
 
 allowEvents:mode
@@ -13362,11 +13558,11 @@
 !XWorkstation class methodsFor:'documentation'!
 
 version
-    ^ '$Header: /cvs/stx/stx/libview/XWorkstation.st,v 1.596 2014-07-22 10:49:13 cg Exp $'
+    ^ '$Header: /cvs/stx/stx/libview/XWorkstation.st,v 1.597 2014-09-20 13:30:53 cg Exp $'
 !
 
 version_CVS
-    ^ '$Header: /cvs/stx/stx/libview/XWorkstation.st,v 1.596 2014-07-22 10:49:13 cg Exp $'
+    ^ '$Header: /cvs/stx/stx/libview/XWorkstation.st,v 1.597 2014-09-20 13:30:53 cg Exp $'
 !
 
 version_SVN