an experiment in making a cocoa webkit browser manageable under X11

more key handling

+75 -20
+1 -1
X11Window.h
··· 11 11 12 12 - (void)updateWKWindowPosition; 13 13 - (void)mainLoopWithWKWindow: (id)wkwobj; 14 - - (void)sendKeyFromXEvent:(XKeyEvent)e; 14 + - (void)sendKeyFromXEvent:(XEvent)e; 15 15 16 16 @end
+74 -19
X11Window.m
··· 62 62 XSync(display, False); 63 63 64 64 if (e.type == KeyPress || e.type == KeyRelease) 65 - [self sendKeyFromXEvent:e.xkey]; 65 + [self sendKeyFromXEvent:e]; 66 66 else 67 67 [self updateWKWindowPosition]; 68 68 } ··· 87 87 waitUntilDone:true]; 88 88 } 89 89 90 - - (void)sendKeyFromXEvent:(XKeyEvent)e 90 + /* translate an x11 key event into an equivalent keycode or string and send 91 + * the NSEvent to the app, which will handle sending it to webkit or the url 92 + * bar */ 93 + - (void)sendKeyFromXEvent:(XEvent)e 91 94 { 92 - char str[257]; 95 + char str[256+1]; 96 + char strNoMod[2] = { '\0' }; 93 97 char *ksname; 94 98 KeySym ks; 95 99 int keycode = 0; 100 + int repeating = 0; 101 + int modifier = 0; 96 102 97 - XLookupString(&e, str, 256, &ks, NULL); 103 + XLookupString(&e.xkey, str, 256, &ks, NULL); 98 104 99 - if (!(ksname = XKeysymToString(ks))) 100 - ksname = "no name"; 105 + /* X11 keysyms in /usr/X11/include/X11/keysymdef.h, carbon key codes 106 + * in /System/Library/Frameworks/Carbon.framework/Frameworks/HIToolbox.framework/Headers/Events.h */ 101 107 102 108 switch (ks) { 103 - case 0x20: keycode = kVK_Space; break; 104 - 105 109 case XK_Control_L: keycode = kVK_Control; break; 106 110 case XK_Control_R: keycode = kVK_RightControl; break; 107 111 case XK_Delete: keycode = kVK_ForwardDelete; break; ··· 110 114 case XK_Escape: keycode = kVK_Escape; break; 111 115 case XK_Home: keycode = kVK_Home; break; 112 116 case XK_Left: keycode = kVK_LeftArrow; break; 117 + case XK_Meta_L: keycode = kVK_Command; break; 118 + case XK_Meta_R: keycode = kVK_Command; break; 119 + case XK_Mode_switch: keycode = kVK_Option; break; 113 120 case XK_Next: keycode = kVK_PageDown; break; 114 121 case XK_Prior: keycode = kVK_PageUp; break; 115 122 case XK_Return: keycode = kVK_Return; break; ··· 119 126 case XK_Up: keycode = kVK_UpArrow; break; 120 127 121 128 default: 122 - if (!strcasecmp(ksname, "backspace")) { keycode = kVK_Delete; break; } 129 + /* translate keys that don't have XK_* equivalents */ 130 + 131 + if (!(ksname = XKeysymToString(ks))) 132 + ksname = "no name"; 133 + 134 + if (!strcasecmp(ksname, "backspace")) { 135 + keycode = kVK_Delete; 136 + break; 137 + } 138 + else if (!strcasecmp(ksname, "space")) { 139 + keycode = kVK_Space; 140 + break; 141 + } 142 + else if (strlen(ksname) == 1) { 143 + strlcpy(str, ksname, sizeof(str)); 144 + strlcpy(strNoMod, ksname, sizeof(strNoMod)); 145 + } 146 + else if (strlen(ksname) > 1) 147 + /* probably a named key */ 148 + fprintf(stderr, "should probably translate \"%s\"\n", 149 + ksname); 123 150 124 151 /* otherwise, assume it's just an ascii letter or number we 125 152 * can pass through as 'characters' param and a 0 keyCode */ 153 + if (debug) 154 + printf("key %s 0x%x (X11 \"%s\") -> key \"%s\"\n", 155 + (e.type == KeyPress ? "press" : "release"), 156 + e.xkey.keycode, 157 + ksname, 158 + str); 126 159 } 127 160 128 - if (debug) 129 - printf("key %s %d (X11 \"%s\") -> keycode %d %s\n", 130 - (e.type == KeyPress ? "press" : "release"), 131 - e.keycode, 132 - ksname, 133 - keycode, 134 - (keycode == 0 ? str : "")); 161 + /* if we are looking at a keyrelease event and there's a pending 162 + * keypress event with the same timestamp, we're holding a key down */ 163 + if (e.type == KeyRelease && XEventsQueued(display, 164 + QueuedAfterReading)) { 165 + XEvent nev; 166 + XPeekEvent(display, &nev); 167 + 168 + if (nev.type == KeyPress && nev.xkey.time == e.xkey.time && 169 + nev.xkey.keycode == e.xkey.keycode) 170 + repeating = 1; 171 + } 172 + 173 + if (e.xkey.state) { 174 + if (e.xkey.state & ShiftMask) 175 + modifier |= NSShiftKeyMask; 176 + if (e.xkey.state & LockMask) 177 + modifier |= NSAlphaShiftKeyMask; 178 + if (e.xkey.state & ControlMask) 179 + modifier |= NSControlKeyMask; 180 + if (e.xkey.state & Mod2Mask) 181 + modifier |= NSCommandKeyMask; 182 + if (e.xkey.state & 0x2000) 183 + modifier |= NSAlternateKeyMask; 184 + 185 + /* TODO: what do mod1, mod3, mod4, and mod5 map to? */ 186 + } 135 187 136 188 NSEvent *fakeEvent = [NSEvent 137 189 keyEventWithType:(e.type == KeyPress ? NSKeyDown : NSKeyUp) 138 190 location:[NSEvent mouseLocation] 139 - modifierFlags:0 191 + modifierFlags:modifier 140 192 timestamp:0 141 193 windowNumber:[[NSApp mainWindow] windowNumber] 142 194 context:nil 143 195 characters:[NSString stringWithFormat:@"%s", str] 144 - charactersIgnoringModifiers:[NSString stringWithFormat:@"%s", str] 145 - isARepeat:NO 196 + charactersIgnoringModifiers:[NSString stringWithFormat:@"%s", strNoMod] 197 + isARepeat:(BOOL)repeating 146 198 keyCode:keycode]; 199 + 200 + if (debug) 201 + NSLog(@"%@", fakeEvent); 147 202 148 203 [NSApp postEvent:fakeEvent atStart:NO]; 149 204 }