an experiment in making a cocoa webkit browser manageable under X11

translate received XEvent key into an NSEvent to send to webkit

add command line arg handling, use last arg as url

+129 -18
+3 -2
WKWindow.h
··· 2 2 #include <WebKit/WebFrame.h> 3 3 #include <WebKit/WebView.h> 4 4 5 - @interface WKWindow : NSObject 5 + @interface WKWindow : NSWindow 6 6 { 7 7 NSWindow *window; 8 8 NSScreen *screen; 9 9 NSRect screen_frame; 10 10 NSTextField *url; 11 11 WebView *browser; 12 + WebFrame *wframe; 12 13 } 13 14 14 15 - (void)setPosition: (NSArray *)aCoords; 15 - - (void)focus; 16 + - (void)loadURL: (NSString *)url; 16 17 17 18 @end
+21 -13
WKWindow.m
··· 11 11 screen = [NSScreen mainScreen]; 12 12 screen_frame = [screen visibleFrame]; 13 13 14 - NSRect coords = NSMakeRect(0, 0, 200, 200); 15 - window = [[[NSWindow alloc] initWithContentRect:coords 14 + NSRect coords = NSMakeRect(0, 0, 300, 300); 15 + window = [[[WKWindow alloc] initWithContentRect:coords 16 16 styleMask:NSBorderlessWindowMask 17 17 backing:NSBackingStoreBuffered 18 18 defer:NO 19 19 screen:screen] autorelease]; 20 - [window setBackgroundColor:[NSColor blueColor]]; 20 + [window setBackgroundColor:[NSColor greenColor]]; 21 + [window setAcceptsMouseMovedEvents:YES]; 21 22 22 - NSRect bframe = NSMakeRect(0, 0, 200, 200); 23 + NSRect bframe = NSMakeRect(0, 0, 300, 300); 23 24 browser = [[WebView alloc] initWithFrame:bframe 24 25 frameName:nil 25 26 groupName:nil]; 26 27 27 28 [window.contentView addSubview:browser]; 28 29 29 - url = [[NSTextField alloc] initWithFrame:NSMakeRect(0, 170, 200, 30)]; 30 + url = [[NSTextField alloc] initWithFrame:NSMakeRect(0, 270, 300, 30)]; 30 31 [window.contentView addSubview:url]; 31 32 32 - WebFrame *wframe = [browser mainFrame]; 33 - [wframe loadRequest:[NSURLRequest requestWithURL:[NSURL 34 - URLWithString:@"https://www.duckduckgo.com/"]]]; 33 + wframe = [browser mainFrame]; 35 34 36 35 [window makeKeyAndOrderFront:window]; 37 36 38 37 return self; 39 38 } 40 39 40 + - (void)loadURL: (NSString *)url 41 + { 42 + [wframe loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:url]]]; 43 + } 44 + 41 45 - (void)setPosition: (NSArray *)aCoords 42 46 { 43 47 int x = [[aCoords objectAtIndex:0] intValue]; ··· 62 66 /* browser's coordinates are relative to the window */ 63 67 [browser setFrame:NSMakeRect(0, 0, width, height - 30)]; 64 68 65 - [self focus]; 69 + [window makeKeyAndOrderFront:window]; 66 70 } 67 71 68 - - (void)focus 69 - { 70 - [window makeKeyAndOrderFront:window]; 71 - [window setOrderedIndex:0]; 72 + /* these are needed because setting styleMask to NSBorderlessWindowMask turns 73 + * them off */ 74 + - (BOOL)canBecomeKeyWindow { 75 + return YES; 76 + } 77 + 78 + - (BOOL)canBecomeMainWindow { 79 + return YES; 72 80 } 73 81 74 82 @end
+1
X11Window.h
··· 11 11 12 12 - (void)updateWKWindowPosition; 13 13 - (void)mainLoopWithWKWindow: (id)wkwobj; 14 + - (void)sendKeyFromXEvent:(XKeyEvent)e; 14 15 15 16 @end
+74 -3
X11Window.m
··· 5 5 #include <stdlib.h> 6 6 #include <stdio.h> 7 7 8 + /* sorry for this */ 9 + #import <Carbon/../Frameworks/HIToolbox.framework/Headers/Events.h> 10 + 8 11 #import "X11Window.h" 9 12 #import "WKWindow.h" 13 + 14 + extern int debug; 10 15 11 16 @implementation X11Window 12 17 ··· 35 40 XSetWMName(display, window, &win_name_prop); 36 41 37 42 XMapWindow(display, window); 38 - XSelectInput(display, window, ExposureMask); 43 + XSelectInput(display, window, KeyPressMask | KeyReleaseMask | 44 + ExposureMask | FocusChangeMask | StructureNotifyMask); 39 45 40 46 XFlush(display); 41 47 XSync(display, False); ··· 53 59 for(;;) { 54 60 XNextEvent(display, &e); 55 61 XFlush(display); 62 + XSync(display, False); 56 63 57 - [self updateWKWindowPosition]; 64 + if (e.type == KeyPress || e.type == KeyRelease) 65 + [self sendKeyFromXEvent:e.xkey]; 66 + else 67 + [self updateWKWindowPosition]; 58 68 } 59 69 60 70 [pool release]; ··· 74 84 75 85 [wkw performSelectorOnMainThread:@selector(setPosition:) 76 86 withObject:pos 77 - waitUntilDone:false]; 87 + waitUntilDone:true]; 88 + } 89 + 90 + - (void)sendKeyFromXEvent:(XKeyEvent)e 91 + { 92 + char str[257]; 93 + char *ksname; 94 + KeySym ks; 95 + int keycode = 0; 96 + 97 + XLookupString(&e, str, 256, &ks, NULL); 98 + 99 + if (!(ksname = XKeysymToString(ks))) 100 + ksname = "no name"; 101 + 102 + switch (ks) { 103 + case 0x20: keycode = kVK_Space; break; 104 + 105 + case XK_Control_L: keycode = kVK_Control; break; 106 + case XK_Control_R: keycode = kVK_RightControl; break; 107 + case XK_Delete: keycode = kVK_ForwardDelete; break; 108 + case XK_Down: keycode = kVK_DownArrow; break; 109 + case XK_End: keycode = kVK_End; break; 110 + case XK_Escape: keycode = kVK_Escape; break; 111 + case XK_Home: keycode = kVK_Home; break; 112 + case XK_Left: keycode = kVK_LeftArrow; break; 113 + case XK_Next: keycode = kVK_PageDown; break; 114 + case XK_Prior: keycode = kVK_PageUp; break; 115 + case XK_Return: keycode = kVK_Return; break; 116 + case XK_Right: keycode = kVK_RightArrow; break; 117 + case XK_Shift_L: keycode = kVK_Shift; break; 118 + case XK_Tab: keycode = kVK_Tab; break; 119 + case XK_Up: keycode = kVK_UpArrow; break; 120 + 121 + default: 122 + if (!strcasecmp(ksname, "backspace")) { keycode = kVK_Delete; break; } 123 + 124 + /* otherwise, assume it's just an ascii letter or number we 125 + * can pass through as 'characters' param and a 0 keyCode */ 126 + } 127 + 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 : "")); 135 + 136 + NSEvent *fakeEvent = [NSEvent 137 + keyEventWithType:(e.type == KeyPress ? NSKeyDown : NSKeyUp) 138 + location:[NSEvent mouseLocation] 139 + modifierFlags:0 140 + timestamp:0 141 + windowNumber:[[NSApp mainWindow] windowNumber] 142 + context:nil 143 + characters:[NSString stringWithFormat:@"%s", str] 144 + charactersIgnoringModifiers:[NSString stringWithFormat:@"%s", str] 145 + isARepeat:NO 146 + keyCode:keycode]; 147 + 148 + [NSApp postEvent:fakeEvent atStart:NO]; 78 149 } 79 150 80 151 @end
+30
main.m
··· 1 1 #include <stdio.h> 2 2 #include <strings.h> 3 + #include <unistd.h> 3 4 4 5 #include <Cocoa/Cocoa.h> 5 6 6 7 #include "X11Window.h" 7 8 #include "WKWindow.h" 9 + 10 + __dead void usage(void); 11 + int debug = 0; 8 12 9 13 int main(int argc, char* argv[]) 10 14 { 15 + int ch; 16 + 11 17 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 12 18 [NSApplication sharedApplication]; 13 19 20 + while ((ch = getopt(argc, argv, "d")) != -1) 21 + switch (ch) { 22 + case 'd': 23 + debug = 1; 24 + break; 25 + default: 26 + usage(); 27 + } 28 + argc -= optind; 29 + argv += optind; 30 + 14 31 /* handle webkit window in the main thread (webkit won't allow use in 15 32 * another thread anyway) */ 16 33 WKWindow *WKW = [WKWindow alloc]; ··· 22 39 [X performSelectorInBackground:@selector(mainLoopWithWKWindow:) 23 40 withObject:WKW]; 24 41 42 + /* if we have a remaining arg, load it as the url */ 43 + if (argc) 44 + [WKW loadURL:[NSString stringWithFormat:@"%s", argv[0]]]; 45 + 25 46 [NSApp run]; 26 47 27 48 [pool release]; 28 49 return (0); 29 50 } 51 + 52 + __dead void 53 + usage(void) 54 + { 55 + extern char *__progname; 56 + 57 + fprintf(stderr, "usage: %s [-d] url\n", __progname); 58 + exit(1); 59 + }