an experiment in making a cocoa webkit browser manageable under X11
at master 207 lines 5.3 kB view raw
1#import <X11/Xlib.h> 2#import <X11/Xutil.h> 3 4#import <err.h> 5#import <stdlib.h> 6#import <stdio.h> 7 8#import "X11Window.h" 9#import "WKWindow.h" 10 11extern int debug; 12 13@implementation X11Window 14 15- (id)init 16{ 17 self = [super init]; 18 19 display = XOpenDisplay(NULL); 20 screen = DefaultScreen(display); 21 22 window = XCreateSimpleWindow( 23 display, 24 RootWindow(display, screen), 25 0, 0, 100, 100, 26 0, 27 BlackPixel(display, screen), 28 BlackPixel(display, screen) 29 ); 30 31 [self setWindowTitle:@"shadowebkit"]; 32 33 XMapWindow(display, window); 34 XSelectInput(display, window, KeyPressMask | KeyReleaseMask | 35 ExposureMask | FocusChangeMask | StructureNotifyMask); 36 37 XFlush(display); 38 XSync(display, False); 39 40 return self; 41} 42 43- (void)mainLoopWithWKWindow: (id)wkwobj 44{ 45 XEvent e; 46 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 47 48 wkw = wkwobj; 49 50 for(;;) { 51 XNextEvent(display, &e); 52 XFlush(display); 53 XSync(display, False); 54 55 if (e.type == KeyPress || e.type == KeyRelease) 56 [self sendKeyFromXEvent:e]; 57 else 58 [self updateWKWindowPosition]; 59 } 60 61 [pool release]; 62} 63 64- (void)setWindowTitle:(NSString *)title 65{ 66 XTextProperty winNameProp; 67 const char *winName = [title UTF8String]; 68 69 if (XStringListToTextProperty(&winName, 1, &winNameProp) == 0) 70 errx(1, "XStringListToTextProperty"); 71 72 XSetWMName(display, window, &winNameProp); 73} 74 75- (void)updateWKWindowPosition 76{ 77 XWindowAttributes xwa; 78 XGetWindowAttributes(display, window, &xwa); 79 80 NSArray *pos = [[NSArray alloc] initWithObjects: 81 [NSNumber numberWithInt:(xwa.x + xwa.border_width)], 82 [NSNumber numberWithInt:(xwa.y + xwa.border_width)], 83 [NSNumber numberWithInt:xwa.width], 84 [NSNumber numberWithInt:xwa.height], 85 nil]; 86 87 [wkw performSelectorOnMainThread:@selector(setPosition:) 88 withObject:pos 89 waitUntilDone:true]; 90} 91 92/* translate an x11 key event into an equivalent keycode or string and send 93 * the NSEvent to the app, which will handle sending it to webkit or the url 94 * bar */ 95- (void)sendKeyFromXEvent:(XEvent)e 96{ 97 char str[256+1]; 98 char strNoMod[2] = { '\0' }; 99 char *ksname; 100 KeySym ks; 101 int keycode = 0; 102 int repeating = 0; 103 int modifier = 0; 104 105 XLookupString(&e.xkey, str, 256, &ks, NULL); 106 107 /* X11 keysyms in /usr/X11/include/X11/keysymdef.h, carbon key codes 108 * in /System/Library/Frameworks/Carbon.framework/Frameworks/HIToolbox.framework/Headers/Events.h */ 109 110 switch (ks) { 111 case XK_Control_L: keycode = kVK_Control; break; 112 case XK_Control_R: keycode = kVK_RightControl; break; 113 case XK_Delete: keycode = kVK_ForwardDelete; break; 114 case XK_Down: keycode = kVK_DownArrow; break; 115 case XK_End: keycode = kVK_End; break; 116 case XK_Escape: keycode = kVK_Escape; break; 117 case XK_Home: keycode = kVK_Home; break; 118 case XK_Left: keycode = kVK_LeftArrow; break; 119 case XK_Meta_L: keycode = kVK_Command; break; 120 case XK_Meta_R: keycode = kVK_Command; break; 121 case XK_Mode_switch: keycode = kVK_Option; break; 122 case XK_Next: keycode = kVK_PageDown; break; 123 case XK_Prior: keycode = kVK_PageUp; break; 124 case XK_Return: keycode = kVK_Return; break; 125 case XK_Right: keycode = kVK_RightArrow; break; 126 case XK_Shift_L: keycode = kVK_Shift; break; 127 case XK_Tab: keycode = kVK_Tab; break; 128 case XK_Up: keycode = kVK_UpArrow; break; 129 130 default: 131 /* translate keys that don't have XK_* equivalents */ 132 133 if (!(ksname = XKeysymToString(ks))) 134 ksname = "no name"; 135 136 if (!strcasecmp(ksname, "backspace")) { 137 keycode = kVK_Delete; 138 break; 139 } 140 else if (!strcasecmp(ksname, "space")) { 141 keycode = kVK_Space; 142 break; 143 } 144 else if (strlen(ksname) == 1) { 145 strlcpy(str, ksname, sizeof(str)); 146 strlcpy(strNoMod, ksname, sizeof(strNoMod)); 147 } 148 else if (strlen(ksname) > 1 && debug) 149 /* probably a named key */ 150 printf("should probably translate \"%s\"\n", ksname); 151 152 /* otherwise, assume it's just an ascii letter or number we 153 * can pass through as 'characters' param and a 0 keyCode */ 154 if (debug) 155 printf("key %s 0x%x (X11 \"%s\") -> key \"%s\"\n", 156 (e.type == KeyPress ? "press" : "release"), 157 e.xkey.keycode, 158 ksname, 159 str); 160 } 161 162 /* if we are looking at a keyrelease event and there's a pending 163 * keypress event with the same timestamp, we're holding a key down */ 164 if (e.type == KeyRelease && XEventsQueued(display, 165 QueuedAfterReading)) { 166 XEvent nev; 167 XPeekEvent(display, &nev); 168 169 if (nev.type == KeyPress && nev.xkey.time == e.xkey.time && 170 nev.xkey.keycode == e.xkey.keycode) 171 repeating = 1; 172 } 173 174 if (e.xkey.state) { 175 if (e.xkey.state & ShiftMask) 176 modifier |= NSShiftKeyMask; 177 if (e.xkey.state & LockMask) 178 modifier |= NSAlphaShiftKeyMask; 179 if (e.xkey.state & ControlMask) 180 modifier |= NSControlKeyMask; 181 if (e.xkey.state & Mod2Mask) 182 modifier |= NSCommandKeyMask; 183 if (e.xkey.state & 0x2000) 184 modifier |= NSAlternateKeyMask; 185 186 /* TODO: what do mod1, mod3, mod4, and mod5 map to? */ 187 } 188 189 NSEvent *fakeEvent = [NSEvent 190 keyEventWithType:(e.type == KeyPress ? NSKeyDown : NSKeyUp) 191 location:[NSEvent mouseLocation] 192 modifierFlags:modifier 193 timestamp:0 194 windowNumber:[[NSApp mainWindow] windowNumber] 195 context:nil 196 characters:[NSString stringWithFormat:@"%s", str] 197 charactersIgnoringModifiers:[NSString stringWithFormat:@"%s", strNoMod] 198 isARepeat:(BOOL)repeating 199 keyCode:keycode]; 200 201 if (debug) 202 NSLog(@"%@", fakeEvent); 203 204 [NSApp postEvent:fakeEvent atStart:NO]; 205} 206 207@end