diff options
author | Petter Rodhelind <petter.rodhelind@gmail.com> | 2020-01-14 11:41:08 +0100 |
---|---|---|
committer | Petter Rodhelind <petter.rodhelind@gmail.com> | 2020-01-14 11:41:08 +0100 |
commit | 02d7aa8915f9c3a3288dab01f321eb94ba219e3b (patch) | |
tree | f053238978479e408a2b83571443e132f30586ab /src/cmd/devdraw | |
parent | c0c9d8f883dfd3a7f5a74499d91bb95884b15873 (diff) | |
parent | 3d1382b98a502d0c34d5ba2c462396acc515016e (diff) | |
download | plan9port-02d7aa8915f9c3a3288dab01f321eb94ba219e3b.tar.gz plan9port-02d7aa8915f9c3a3288dab01f321eb94ba219e3b.tar.bz2 plan9port-02d7aa8915f9c3a3288dab01f321eb94ba219e3b.zip |
Merge remote-tracking branch 'upstream/master'
Diffstat (limited to 'src/cmd/devdraw')
39 files changed, 3299 insertions, 5734 deletions
diff --git a/src/cmd/devdraw/bigarrow.h b/src/cmd/devdraw/bigarrow.h index 1221ec8c..4bfe0245 100644 --- a/src/cmd/devdraw/bigarrow.h +++ b/src/cmd/devdraw/bigarrow.h @@ -14,70 +14,70 @@ Cursor bigarrow = { Cursor2 bigarrow2 = { { -2, -2 }, - { 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, - 0xC0, 0x00, 0x00, 0x03, - 0xC0, 0x00, 0x00, 0x07, - 0xC0, 0x00, 0x00, 0x1E, - 0xC0, 0x00, 0x00, 0x3C, - 0xC0, 0x00, 0x00, 0xF0, - 0xC0, 0x00, 0x03, 0xE0, - 0xC0, 0x00, 0x0F, 0x80, - 0xC0, 0x00, 0x0E, 0x00, - 0xC0, 0x00, 0x07, 0x00, - 0xC0, 0x00, 0x03, 0x80, - 0xC0, 0x00, 0x01, 0xC0, - 0xC0, 0x00, 0x00, 0xE0, - 0xC0, 0x00, 0x00, 0x70, - 0xC0, 0x00, 0x00, 0x38, - 0xC0, 0x00, 0x00, 0x1C, - 0xC0, 0x00, 0x00, 0x0E, - 0xC0, 0x00, 0x00, 0x07, - 0xC0, 0x00, 0x00, 0x03, - 0xC0, 0xC0, 0x00, 0x07, - 0xC0, 0xE0, 0x00, 0x0E, - 0xC1, 0xF0, 0x00, 0x1C, - 0xC1, 0xB8, 0x00, 0x38, - 0xC3, 0x9C, 0x00, 0x70, - 0xC3, 0x0E, 0x00, 0xE0, - 0xC7, 0x07, 0x01, 0xC0, - 0xCE, 0x03, 0x83, 0x80, - 0xCC, 0x01, 0xC7, 0x00, - 0xDC, 0x00, 0xEE, 0x00, - 0xF8, 0x00, 0x7C, 0x00, + { 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, + 0xC0, 0x00, 0x00, 0x03, + 0xC0, 0x00, 0x00, 0x07, + 0xC0, 0x00, 0x00, 0x1E, + 0xC0, 0x00, 0x00, 0x3C, + 0xC0, 0x00, 0x00, 0xF0, + 0xC0, 0x00, 0x03, 0xE0, + 0xC0, 0x00, 0x0F, 0x80, + 0xC0, 0x00, 0x0E, 0x00, + 0xC0, 0x00, 0x07, 0x00, + 0xC0, 0x00, 0x03, 0x80, + 0xC0, 0x00, 0x01, 0xC0, + 0xC0, 0x00, 0x00, 0xE0, + 0xC0, 0x00, 0x00, 0x70, + 0xC0, 0x00, 0x00, 0x38, + 0xC0, 0x00, 0x00, 0x1C, + 0xC0, 0x00, 0x00, 0x0E, + 0xC0, 0x00, 0x00, 0x07, + 0xC0, 0x00, 0x00, 0x03, + 0xC0, 0xC0, 0x00, 0x07, + 0xC0, 0xE0, 0x00, 0x0E, + 0xC1, 0xF0, 0x00, 0x1C, + 0xC1, 0xB8, 0x00, 0x38, + 0xC3, 0x9C, 0x00, 0x70, + 0xC3, 0x0E, 0x00, 0xE0, + 0xC7, 0x07, 0x01, 0xC0, + 0xCE, 0x03, 0x83, 0x80, + 0xCC, 0x01, 0xC7, 0x00, + 0xDC, 0x00, 0xEE, 0x00, + 0xF8, 0x00, 0x7C, 0x00, 0xF0, 0x00, 0x38, 0x00, }, - { 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x3F, 0xFF, 0xFF, 0xFC, - 0x3F, 0xFF, 0xFF, 0xF8, - 0x3F, 0xFF, 0xFF, 0xE0, - 0x3F, 0xFF, 0xFF, 0xC0, - 0x3F, 0xFF, 0xFF, 0x00, - 0x3F, 0xFF, 0xFC, 0x00, - 0x3F, 0xFF, 0xF0, 0x00, - 0x3F, 0xFF, 0xF0, 0x00, - 0x3F, 0xFF, 0xF8, 0x00, - 0x3F, 0xFF, 0xFC, 0x00, - 0x3F, 0xFF, 0xFE, 0x00, - 0x3F, 0xFF, 0xFF, 0x00, - 0x3F, 0xFF, 0xFF, 0x80, - 0x3F, 0xFF, 0xFF, 0xC0, - 0x3F, 0xFF, 0xFF, 0xE0, - 0x3F, 0xFF, 0xFF, 0xF0, - 0x3F, 0xFF, 0xFF, 0xF8, - 0x3F, 0xFF, 0xFF, 0xFC, - 0x3F, 0x3F, 0xFF, 0xF8, - 0x3F, 0x1F, 0xFF, 0xF0, - 0x3E, 0x0F, 0xFF, 0xE0, - 0x3E, 0x07, 0xFF, 0xC0, - 0x3C, 0x03, 0xFF, 0x80, - 0x3C, 0x01, 0xFF, 0x00, - 0x38, 0x00, 0xFE, 0x00, - 0x30, 0x00, 0x7C, 0x00, - 0x30, 0x00, 0x38, 0x00, - 0x20, 0x00, 0x10, 0x00, - 0x00, 0x00, 0x00, 0x00, + { 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x3F, 0xFF, 0xFF, 0xFC, + 0x3F, 0xFF, 0xFF, 0xF8, + 0x3F, 0xFF, 0xFF, 0xE0, + 0x3F, 0xFF, 0xFF, 0xC0, + 0x3F, 0xFF, 0xFF, 0x00, + 0x3F, 0xFF, 0xFC, 0x00, + 0x3F, 0xFF, 0xF0, 0x00, + 0x3F, 0xFF, 0xF0, 0x00, + 0x3F, 0xFF, 0xF8, 0x00, + 0x3F, 0xFF, 0xFC, 0x00, + 0x3F, 0xFF, 0xFE, 0x00, + 0x3F, 0xFF, 0xFF, 0x00, + 0x3F, 0xFF, 0xFF, 0x80, + 0x3F, 0xFF, 0xFF, 0xC0, + 0x3F, 0xFF, 0xFF, 0xE0, + 0x3F, 0xFF, 0xFF, 0xF0, + 0x3F, 0xFF, 0xFF, 0xF8, + 0x3F, 0xFF, 0xFF, 0xFC, + 0x3F, 0x3F, 0xFF, 0xF8, + 0x3F, 0x1F, 0xFF, 0xF0, + 0x3E, 0x0F, 0xFF, 0xE0, + 0x3E, 0x07, 0xFF, 0xC0, + 0x3C, 0x03, 0xFF, 0x80, + 0x3C, 0x01, 0xFF, 0x00, + 0x38, 0x00, 0xFE, 0x00, + 0x30, 0x00, 0x7C, 0x00, + 0x30, 0x00, 0x38, 0x00, + 0x20, 0x00, 0x10, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, } }; diff --git a/src/cmd/devdraw/cocoa-screen.h b/src/cmd/devdraw/cocoa-screen.h deleted file mode 100644 index b5e3c701..00000000 --- a/src/cmd/devdraw/cocoa-screen.h +++ /dev/null @@ -1,24 +0,0 @@ -#define setcursor dsetcursor - -Memimage *attachscreen(char*, char*); -void setmouse(Point); -void setcursor(Cursor*, Cursor2*); -void setlabel(char*); -char* getsnarf(void); -void putsnarf(char*); -void topwin(void); - -void mousetrack(int, int, int, uint); -void keystroke(int); -void kicklabel(char*); - -void servep9p(void); -void zlock(void); -void zunlock(void); - -void resizeimg(void); - -Rectangle mouserect; - -int mouseresized; -void resizewindow(Rectangle); diff --git a/src/cmd/devdraw/cocoa-screen.m b/src/cmd/devdraw/cocoa-screen.m deleted file mode 100644 index 9b404c67..00000000 --- a/src/cmd/devdraw/cocoa-screen.m +++ /dev/null @@ -1,1674 +0,0 @@ -/* - * Cocoa's event loop must be in main thread. - * - * Unless otherwise stated, all coordinate systems - * are bottom-left-based. - */ - -#define Cursor OSXCursor -#define Point OSXPoint -#define Rect OSXRect - -#import <Cocoa/Cocoa.h> - -#undef Cursor -#undef Point -#undef Rect - -#include <u.h> -#include <libc.h> -#include "cocoa-thread.h" -#include <draw.h> -#include <memdraw.h> -#include <keyboard.h> -#include <cursor.h> -#include "cocoa-screen.h" -#include "osx-keycodes.h" -#include "devdraw.h" -#include "bigarrow.h" -#include "glendapng.h" - -// Use non-deprecated names. -#if MAC_OS_X_VERSION_MAX_ALLOWED >= 101200 -#define NSKeyDown NSEventTypeKeyDown -#define NSShiftKeyMask NSEventModifierFlagShift -#define NSAlternateKeyMask NSEventModifierFlagOption -#define NSCommandKeyMask NSEventModifierFlagCommand -#define NSResizableWindowMask NSWindowStyleMaskResizable -#define NSLeftMouseDown NSEventTypeLeftMouseDown -#define NSLeftMouseUp NSEventTypeLeftMouseUp -#define NSRightMouseDown NSEventTypeRightMouseDown -#define NSRightMouseUp NSEventTypeRightMouseUp -#define NSOtherMouseDown NSEventTypeOtherMouseDown -#define NSOtherMouseUp NSEventTypeOtherMouseUp -#define NSScrollWheel NSEventTypeScrollWheel -#define NSMouseMoved NSEventTypeMouseMoved -#define NSLeftMouseDragged NSEventTypeLeftMouseDragged -#define NSRightMouseDragged NSEventTypeRightMouseDragged -#define NSOtherMouseDragged NSEventTypeOtherMouseDragged -#define NSCompositeCopy NSCompositingOperationCopy -#define NSCompositeSourceIn NSCompositingOperationSourceIn -#define NSFlagsChanged NSEventTypeFlagsChanged -#define NSTitledWindowMask NSWindowStyleMaskTitled -#define NSClosableWindowMask NSWindowStyleMaskClosable -#define NSMiniaturizableWindowMask NSWindowStyleMaskMiniaturizable -#define NSBorderlessWindowMask NSWindowStyleMaskBorderless -#endif - -AUTOFRAMEWORK(Cocoa) - -#define LOG if(0)NSLog -#define panic sysfatal - -int usegestures = 0; -int useliveresizing = 0; -int useoldfullscreen = 1; -int usebigarrow = 0; - -static void setprocname(const char*); - -/* - * By default, devdraw uses retina displays. - * Set devdrawretina=0 in the environment to override. - */ -int devdrawretina = 1; - -void -usage(void) -{ - fprint(2, "usage: devdraw (don't run directly)\n"); - threadexitsall("usage"); -} - -@interface appdelegate : NSObject<NSApplicationDelegate,NSWindowDelegate> @end - -NSObject<NSApplicationDelegate,NSWindowDelegate> *myApp; - -void -threadmain(int argc, char **argv) -{ - char *envvar; - - /* - * Move the protocol off stdin/stdout so that - * any inadvertent prints don't screw things up. - */ - dup(0,3); - dup(1,4); - close(0); - close(1); - open("/dev/null", OREAD); - open("/dev/null", OWRITE); - - ARGBEGIN{ - case 'D': /* for good ps -a listings */ - break; - case 'f': - useoldfullscreen = 1; - break; - case 'g': - usegestures = 1; - break; - case 'b': - usebigarrow = 1; - break; - default: - usage(); - }ARGEND - - setprocname(argv0); - - if (envvar = getenv("devdrawretina")) - devdrawretina = atoi(envvar) > 0; - - if(OSX_VERSION < 100700) - [NSAutoreleasePool new]; - - [NSApplication sharedApplication]; - [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; - myApp = [appdelegate new]; - [NSApp setDelegate:myApp]; - [NSApp run]; -} - -#define WIN win.ofs[win.isofs] - -struct -{ - NSWindow *ofs[2]; /* ofs[1] for old fullscreen; ofs[0] else */ - int isofs; - int isnfs; - NSView *content; - NSBitmapImageRep *img; - int needimg; - int deferflush; - NSCursor *cursor; - CGFloat topointscale; - CGFloat topixelscale; -} win; - -struct -{ - NSCursor *bigarrow; - int kbuttons; - int mbuttons; - NSPoint mpos; - int mscroll; - int willactivate; -} in; - -static void hidebars(int); -static void flushimg(NSRect); -static void autoflushwin(int); -static void flushwin(void); -static void followzoombutton(NSRect); -static void getmousepos(void); -static void makeicon(void); -static void makemenu(void); -static void makewin(char*); -static void sendmouse(void); -static void kicklabel0(char*); -static void setcursor0(Cursor*); -static void togglefs(void); -static void acceptresizing(int); - -static NSCursor* makecursor(Cursor*); - -static NSSize winsizepixels(); -static NSSize winsizepoints(); -static NSRect scalerect(NSRect, CGFloat); -static NSPoint scalepoint(NSPoint, CGFloat); -static NSRect dilate(NSRect); - -@implementation appdelegate -- (void)applicationDidFinishLaunching:(id)arg -{ - in.bigarrow = makecursor(&bigarrow); - makeicon(); - makemenu(); - [NSApplication - detachDrawingThread:@selector(callservep9p:) - toTarget:[self class] withObject:nil]; -} - -- (void)windowDidBecomeKey:(id)arg -{ - getmousepos(); - sendmouse(); -} -- (void)windowDidResize:(id)arg -{ - getmousepos(); - sendmouse(); -} -- (void)windowWillStartLiveResize:(id)arg -{ - if(useliveresizing == 0) - [win.content setHidden:YES]; -} -- (void)windowDidEndLiveResize:(id)arg -{ - if(useliveresizing == 0) - [win.content setHidden:NO]; -} -- (void)windowDidChangeScreen:(id)arg -{ - if(win.isnfs || win.isofs) - hidebars(1); - [win.ofs[1] setFrame:[[WIN screen] frame] display:YES]; -} -- (BOOL)windowShouldZoom:(id)arg toFrame:(NSRect)r -{ - followzoombutton(r); - return YES; -} -- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(id)arg -{ - return YES; -} -- (void)applicationDidBecomeActive:(id)arg{ in.willactivate = 0;} -- (void)windowWillEnterFullScreen:(id)arg{ acceptresizing(1);} -- (void)windowDidEnterFullScreen:(id)arg{ win.isnfs = 1; hidebars(1);} -- (void)windowWillExitFullScreen:(id)arg{ win.isnfs = 0; hidebars(0);} -- (void)windowDidExitFullScreen:(id)arg -{ - NSButton *b; - - b = [WIN standardWindowButton:NSWindowMiniaturizeButton]; - - if([b isEnabled] == 0){ - [b setEnabled:YES]; - hidebars(0); - } -} -- (void)windowWillClose:(id)arg -{ - autoflushwin(0); /* can crash otherwise */ -} - -+ (void)callservep9p:(id)arg -{ - servep9p(); - [NSApp terminate:self]; -} -- (void)plumbmanual:(id)arg -{ - if(fork() != 0) - return; - execl("plumb", "plumb", "devdraw(1)", nil); -} -+ (void)callflushwin:(id)arg{ flushwin();} -- (void)calltogglefs:(id)arg{ togglefs();} - -+ (void)callflushimg:(NSValue*)v{ flushimg([v rectValue]);} -+ (void)callmakewin:(NSValue*)v{ makewin([v pointerValue]);} -+ (void)callsetcursor0:(NSValue*)v{ setcursor0([v pointerValue]);} -+ (void)callkicklabel0:(NSValue*)v{ kicklabel0([v pointerValue]);} -@end - -static Memimage* initimg(void); - -Memimage* -attachscreen(char *label, char *winsize) -{ - static int first = 1; - - if(first) - first = 0; - else - panic("attachscreen called twice"); - - if(label == nil) - label = "gnot a label"; - if(strcmp(label, "page") == 0) - useliveresizing = 1; - - /* - * Create window in main thread, else no cursor - * change while resizing. - */ - [appdelegate - performSelectorOnMainThread:@selector(callmakewin:) - withObject:[NSValue valueWithPointer:winsize] - waitUntilDone:YES]; -// makewin(winsize); - - kicklabel(label); - return initimg(); -} - -@interface appwin : NSWindow @end -@interface contentview : NSView @end - -@implementation appwin -- (NSTimeInterval)animationResizeTime:(NSRect)r -{ - return 0; -} -- (BOOL)canBecomeKeyWindow -{ - return YES; /* else no keyboard for old fullscreen */ -} -- (void)makeKeyAndOrderFront:(id)arg -{ - LOG(@"makeKeyAndOrderFront"); - - autoflushwin(1); - [win.content setHidden:NO]; - [super makeKeyAndOrderFront:arg]; -} -- (void)miniaturize:(id)arg -{ - [super miniaturize:arg]; - [NSApp hide:nil]; - - [win.content setHidden:YES]; - autoflushwin(0); -} -- (void)deminiaturize:(id)arg -{ - autoflushwin(1); - [win.content setHidden:NO]; - [super deminiaturize:arg]; -} - -- (NSDragOperation)draggingEntered:(id)arg -{ - NSPasteboard *b; - NSDragOperation op; - - op = [arg draggingSourceOperationMask]; - b = [arg draggingPasteboard]; - - if([[b types] containsObject:NSFilenamesPboardType]) - if(op&NSDragOperationLink) - return NSDragOperationLink; - - return NSDragOperationNone; -} - -- (BOOL)performDragOperation:(id)arg -{ - NSPasteboard *b; - NSArray *files; - int i, n; - - b = [arg draggingPasteboard]; - if(![[b types] containsObject:NSFilenamesPboardType]) - return NO; - - files = [b propertyListForType:NSFilenamesPboardType]; - n = [files count]; - for(i=0; i<n; i++) - if(fork() == 0) - execl("macedit", "macedit", [[files objectAtIndex:i] UTF8String], nil); - - return YES; -} - -@end - -double -min(double a, double b) -{ - return a<b? a : b; -} - -enum -{ - Winstyle = NSTitledWindowMask - | NSClosableWindowMask - | NSMiniaturizableWindowMask - | NSResizableWindowMask -}; - -static void -makewin(char *s) -{ - NSRect r, sr; - NSWindow *w; - Rectangle wr; - int i, set; - - sr = [[NSScreen mainScreen] frame]; - r = [[NSScreen mainScreen] visibleFrame]; - - if(s && *s){ - if(parsewinsize(s, &wr, &set) < 0) - sysfatal("%r"); - }else{ - wr = Rect(0, 0, sr.size.width*2/3, sr.size.height*2/3); - set = 0; - } - - r.origin.x = wr.min.x; - r.origin.y = sr.size.height-wr.max.y; /* winsize is top-left-based */ - r.size.width = min(Dx(wr), r.size.width); - r.size.height = min(Dy(wr), r.size.height); - r = [NSWindow contentRectForFrameRect:r - styleMask:Winstyle]; - - w = [[appwin alloc] - initWithContentRect:r - styleMask:Winstyle - backing:NSBackingStoreBuffered defer:NO]; - [w setTitle:@"devdraw"]; - - if(!set) - [w center]; -#if OSX_VERSION >= 100700 - [w setCollectionBehavior: - NSWindowCollectionBehaviorFullScreenPrimary]; -#endif - [w setContentMinSize:NSMakeSize(128,128)]; - - [w registerForDraggedTypes:[NSArray arrayWithObjects: - NSFilenamesPboardType, nil]]; - - win.ofs[0] = w; - win.ofs[1] = [[appwin alloc] - initWithContentRect:sr - styleMask:NSBorderlessWindowMask - backing:NSBackingStoreBuffered defer:YES]; - for(i=0; i<2; i++){ - [win.ofs[i] setAcceptsMouseMovedEvents:YES]; - [win.ofs[i] setDelegate:myApp]; - [win.ofs[i] setDisplaysWhenScreenProfileChanges:NO]; - } - win.isofs = 0; - win.content = [contentview new]; - [WIN setContentView:win.content]; - - topwin(); -} - -static Memimage* -initimg(void) -{ - Memimage *i; - NSSize size, ptsize; - Rectangle r; - - size = winsizepixels(); - LOG(@"initimg %.0f %.0f", size.width, size.height); - - r = Rect(0, 0, size.width, size.height); - i = allocmemimage(r, XBGR32); - if(i == nil) - panic("allocmemimage: %r"); - if(i->data == nil) - panic("i->data == nil"); - - win.img = [[NSBitmapImageRep alloc] - initWithBitmapDataPlanes:&i->data->bdata - pixelsWide:Dx(r) - pixelsHigh:Dy(r) - bitsPerSample:8 - samplesPerPixel:3 - hasAlpha:NO - isPlanar:NO - colorSpaceName:NSDeviceRGBColorSpace - bytesPerRow:bytesperline(r, 32) - bitsPerPixel:32]; - ptsize = winsizepoints(); - [win.img setSize: ptsize]; - win.topixelscale = size.width / ptsize.width; - win.topointscale = 1.0f / win.topixelscale; - - // NOTE: This is not really the display DPI. - // On retina, topixelscale is 2; otherwise it is 1. - // This formula gives us 220 for retina, 110 otherwise. - // That's not quite right but it's close to correct. - // http://en.wikipedia.org/wiki/List_of_displays_by_pixel_density#Apple - displaydpi = win.topixelscale * 110; - - return i; -} - -void -resizeimg(void) -{ - [win.img release]; - _drawreplacescreenimage(initimg()); - - mouseresized = 1; - sendmouse(); -} - -static void -waitimg(int msec) -{ - NSDate *limit; - int n; - - win.needimg = 1; - win.deferflush = 0; - - n = 0; - limit = [NSDate dateWithTimeIntervalSinceNow:msec/1000.0]; - do{ - [[NSRunLoop currentRunLoop] - runMode:@"waiting image" - beforeDate:limit]; - n++; - }while(win.needimg && [(NSDate*)[NSDate date] compare:limit]<0); - - win.deferflush = win.needimg; - - LOG(@"waitimg %s (%d loop)", win.needimg?"defer":"ok", n); -} - -void -_flushmemscreen(Rectangle r) -{ - static int n; - NSRect rect; - - LOG(@"_flushmemscreen"); - - if(n==0){ - n++; - return; /* to skip useless white init rect */ - }else - if(n==1){ - [WIN performSelectorOnMainThread: - @selector(makeKeyAndOrderFront:) - withObject:nil - waitUntilDone:NO]; - n++; - }else - if([win.content canDraw] == 0) - return; - - rect = NSMakeRect(r.min.x, r.min.y, Dx(r), Dy(r)); - - // This can get blocked behind responding to mouse events, - // which need to acquire the zlock, so let go of it during - // the flush. Perhaps the waitUntilDone:YES is wrong? - zunlock(); - [appdelegate - performSelectorOnMainThread:@selector(callflushimg:) - withObject:[NSValue valueWithRect:rect] - waitUntilDone:YES - modes:[NSArray arrayWithObjects: - NSRunLoopCommonModes, - @"waiting image", nil]]; - zlock(); -} - -static void drawimg(NSRect, uint); -static void drawresizehandle(void); - -enum -{ - Pixel = 1, - Barsize = 4*Pixel, - Cornersize = 3*Pixel, - Handlesize = 3*Barsize + 1*Pixel, -}; - -/* - * |rect| is in pixel coordinates. - */ -static void -flushimg(NSRect rect) -{ - NSRect dr, r; - - if([win.content lockFocusIfCanDraw] == 0) - return; - - if(win.needimg){ - if(!NSEqualSizes(scalerect(rect, win.topointscale).size, [win.img size])){ - LOG(@"flushimg reject %.0f %.0f", - rect.size.width, rect.size.height); - [win.content unlockFocus]; - return; - } - win.needimg = 0; - }else - win.deferflush = 1; - - LOG(@"flushimg ok %.0f %.0f", rect.size.width, rect.size.height); - - /* - * Unless we are inside "drawRect", we have to round - * the corners ourselves, if this is the custom. - * "NSCompositeSourceIn" can do that, but we don't - * apply it to the whole rectangle, because this - * slows down trackpad scrolling considerably in - * Acme. - */ - r = [win.content bounds]; - rect = dilate(scalerect(rect, win.topointscale)); - r.size.height -= Cornersize; - dr = NSIntersectionRect(r, rect); - LOG(@"r %.0f %.0f %.0f %.0f", r.origin.x, r.origin.y, rect.size.width, rect.size.height); - LOG(@"rect in points %f %f %.0f %.0f", rect.origin.x, rect.origin.y, rect.size.width, rect.size.height); - LOG(@"dr in points %f %f %.0f %.0f", dr.origin.x, dr.origin.y, dr.size.width, dr.size.height); - drawimg(dr, NSCompositeCopy); - - r.origin.y = r.size.height; - r.size = NSMakeSize(Cornersize, Cornersize); - dr = NSIntersectionRect(r, rect); - drawimg(dr, NSCompositeSourceIn); - - r.origin.x = [win.img size].width - Cornersize; - dr = NSIntersectionRect(r, rect); - drawimg(dr, NSCompositeSourceIn); - - r.size.width = r.origin.x - Cornersize; - r.origin.x -= r.size.width; - dr = NSIntersectionRect(r, rect); - drawimg(dr, NSCompositeCopy); - - if(OSX_VERSION<100700 && win.isofs==0){ - r.origin.x = [win.img size].width - Handlesize; - r.origin.y = [win.img size].height - Handlesize; - r.size = NSMakeSize(Handlesize, Handlesize); - if(NSIntersectsRect(r, rect)) - drawresizehandle(); - } - [win.content unlockFocus]; -} - -static void -autoflushwin(int set) -{ - static NSTimer *t; - - if(set){ - if(t) - return; - /* - * We need "NSRunLoopCommonModes", otherwise the - * timer will not fire during live resizing. - */ - t = [NSTimer - timerWithTimeInterval:0.033 - target:[appdelegate class] - selector:@selector(callflushwin:) userInfo:nil - repeats:YES]; - [[NSRunLoop currentRunLoop] addTimer:t - forMode:NSRunLoopCommonModes]; - }else{ - [t invalidate]; - t = nil; - win.deferflush = 0; - } -} - -static void -flushwin(void) -{ - if(win.deferflush && win.needimg==0){ - [WIN flushWindow]; - win.deferflush = 0; - } -} - -/* - * |dr| is sized in points. What if I make it pixels? - */ -static void -drawimg(NSRect dr, uint op) -{ - CGContextRef c; - CGImageRef i; - NSRect sr; - - if(NSIsEmptyRect(dr)) - return; - - sr = [win.content convertRect:dr fromView:nil]; - LOG(@"before dr: %f %f %f %f\n", dr.origin.x, dr.origin.y, dr.size.width, dr.size.height); - LOG(@"before sr: %f %f %f %f\n", sr.origin.x, sr.origin.y, sr.size.width, sr.size.height); - - dr = scalerect(dr, win.topixelscale); - sr = scalerect(sr, win.topixelscale); - - LOG(@"dr: %f %f %f %f\n", dr.origin.x, dr.origin.y, dr.size.width, dr.size.height); - LOG(@"sr: %f %f %f %f\n", sr.origin.x, sr.origin.y, sr.size.width, sr.size.height); - if(OSX_VERSION >= 100800){ - i = CGImageCreateWithImageInRect([win.img CGImage], NSRectToCGRect(dr)); - c = [[WIN graphicsContext] graphicsPort]; - - CGContextSaveGState(c); - if(op == NSCompositeSourceIn) - CGContextSetBlendMode(c, kCGBlendModeSourceIn); - LOG(@"wim.img size %f %f\n", [win.img size].width, [win.img size].height); - CGContextTranslateCTM(c, 0, [win.img size].height); - CGContextScaleCTM(c, win.topointscale, -win.topointscale); - CGContextDrawImage(c, NSRectToCGRect(sr), i); - CGContextRestoreGState(c); - - CGImageRelease(i); - }else{ - [win.img drawInRect:dr fromRect:sr - operation:op fraction:1 - respectFlipped:YES hints:nil]; - } -// NSFrameRect(dr); -} - -static void -drawresizehandle(void) -{ - NSColor *color[Barsize]; - NSPoint a,b; - Point c; - int i,j; - - c = Pt([win.img size].width, [win.img size].height); - - [[WIN graphicsContext] setShouldAntialias:NO]; - - color[0] = [NSColor clearColor]; - color[1] = [NSColor darkGrayColor]; - color[2] = [NSColor lightGrayColor]; - color[3] = [NSColor whiteColor]; - - for(i=1; i+Barsize <= Handlesize; ) - for(j=0; j<Barsize; j++){ - [color[j] setStroke]; - i++; - a = NSMakePoint(c.x-i, c.y-1); - b = NSMakePoint(c.x-2, c.y+1-i); - [NSBezierPath strokeLineFromPoint:a toPoint:b]; - } -} - -static void getgesture(NSEvent*); -static void getkeyboard(NSEvent*); -static void getmouse(NSEvent*); -static void gettouch(NSEvent*, int); -static void updatecursor(void); - -@implementation contentview -/* - * "drawRect" is called each time Cocoa needs an - * image, and each time we call "display". It is - * preceded by background painting, and followed by - * "flushWindow". - */ -- (void)drawRect:(NSRect)r -{ - static int first = 1; - - LOG(@"drawrect %.0f %.0f %.0f %.0f", - r.origin.x, r.origin.y, r.size.width, r.size.height); - - if(first) - first = 0; - else - resizeimg(); - - if([WIN inLiveResize]) - waitimg(100); - else - waitimg(500); -} -- (BOOL)isFlipped -{ - return YES; /* to make the content's origin top left */ -} -- (BOOL)acceptsFirstResponder -{ - return YES; /* else no keyboard */ -} -- (id)initWithFrame:(NSRect)r -{ - [super initWithFrame:r]; - [self setAcceptsTouchEvents:YES]; - [self setHidden:YES]; /* to avoid early "drawRect" call */ - return self; -} -- (void)setHidden:(BOOL)set -{ - if(!set) - [WIN makeFirstResponder:self]; /* for keyboard focus */ - [super setHidden:set]; -} -- (void)cursorUpdate:(NSEvent*)e{ updatecursor();} - -- (void)mouseMoved:(NSEvent*)e{ getmouse(e);} -- (void)mouseDown:(NSEvent*)e{ getmouse(e);} -- (void)mouseDragged:(NSEvent*)e{ getmouse(e);} -- (void)mouseUp:(NSEvent*)e{ getmouse(e);} -- (void)otherMouseDown:(NSEvent*)e{ getmouse(e);} -- (void)otherMouseDragged:(NSEvent*)e{ getmouse(e);} -- (void)otherMouseUp:(NSEvent*)e{ getmouse(e);} -- (void)rightMouseDown:(NSEvent*)e{ getmouse(e);} -- (void)rightMouseDragged:(NSEvent*)e{ getmouse(e);} -- (void)rightMouseUp:(NSEvent*)e{ getmouse(e);} -- (void)scrollWheel:(NSEvent*)e{ getmouse(e);} - -- (void)keyDown:(NSEvent*)e{ getkeyboard(e);} -- (void)flagsChanged:(NSEvent*)e{ getkeyboard(e);} - -- (void)magnifyWithEvent:(NSEvent*)e{ getgesture(e);} - -- (void)touchesBeganWithEvent:(NSEvent*)e -{ - gettouch(e, NSTouchPhaseBegan); -} -- (void)touchesMovedWithEvent:(NSEvent*)e -{ - gettouch(e, NSTouchPhaseMoved); -} -- (void)touchesEndedWithEvent:(NSEvent*)e -{ - gettouch(e, NSTouchPhaseEnded); -} -- (void)touchesCancelledWithEvent:(NSEvent*)e -{ - gettouch(e, NSTouchPhaseCancelled); -} -@end - -static int keycvt[] = -{ - [QZ_IBOOK_ENTER]= '\n', - [QZ_RETURN]= '\n', - [QZ_ESCAPE]= 27, - [QZ_BACKSPACE]= '\b', - [QZ_LALT]= Kalt, - [QZ_LCTRL]= Kctl, - [QZ_LSHIFT]= Kshift, - [QZ_F1]= KF+1, - [QZ_F2]= KF+2, - [QZ_F3]= KF+3, - [QZ_F4]= KF+4, - [QZ_F5]= KF+5, - [QZ_F6]= KF+6, - [QZ_F7]= KF+7, - [QZ_F8]= KF+8, - [QZ_F9]= KF+9, - [QZ_F10]= KF+10, - [QZ_F11]= KF+11, - [QZ_F12]= KF+12, - [QZ_INSERT]= Kins, - [QZ_DELETE]= 0x7F, - [QZ_HOME]= Khome, - [QZ_END]= Kend, - [QZ_KP_PLUS]= '+', - [QZ_KP_MINUS]= '-', - [QZ_TAB]= '\t', - [QZ_PAGEUP]= Kpgup, - [QZ_PAGEDOWN]= Kpgdown, - [QZ_UP]= Kup, - [QZ_DOWN]= Kdown, - [QZ_LEFT]= Kleft, - [QZ_RIGHT]= Kright, - [QZ_KP_MULTIPLY]= '*', - [QZ_KP_DIVIDE]= '/', - [QZ_KP_ENTER]= '\n', - [QZ_KP_PERIOD]= '.', - [QZ_KP0]= '0', - [QZ_KP1]= '1', - [QZ_KP2]= '2', - [QZ_KP3]= '3', - [QZ_KP4]= '4', - [QZ_KP5]= '5', - [QZ_KP6]= '6', - [QZ_KP7]= '7', - [QZ_KP8]= '8', - [QZ_KP9]= '9', -}; - -@interface apptext : NSTextView @end - -@implementation apptext -- (void)doCommandBySelector:(SEL)s{} /* Esc key beeps otherwise */ -- (void)insertText:(id)arg{} /* to avoid a latency after some time */ -@end - -static void -interpretdeadkey(NSEvent *e) -{ - static apptext *t; - - if(t == nil) - t = [apptext new]; - [t interpretKeyEvents:[NSArray arrayWithObject:e]]; -} - -static void -getkeyboard(NSEvent *e) -{ - static int omod; - NSString *s; - char c; - int k, m; - uint code; - - m = [e modifierFlags]; - - switch([e type]){ - case NSKeyDown: - s = [e characters]; - c = [s UTF8String][0]; - - interpretdeadkey(e); - - if(m & NSCommandKeyMask){ - if((m & NSShiftKeyMask) && 'a' <= c && c <= 'z') - c += 'A' - 'a'; - if(' '<=c && c<='~') - keystroke(Kcmd+c); - break; - } - k = c; - code = [e keyCode]; - if(code<nelem(keycvt) && keycvt[code]) - k = keycvt[code]; - if(k==0) - break; - if(k>0) - keystroke(k); - else - keystroke([s characterAtIndex:0]); - break; - - case NSFlagsChanged: - if(in.mbuttons || in.kbuttons){ - in.kbuttons = 0; - if(m & NSControlKeyMask) - in.kbuttons |= 1; - if(m & NSAlternateKeyMask) - in.kbuttons |= 2; - if(m & NSCommandKeyMask) - in.kbuttons |= 4; - sendmouse(); - }else - if(m&NSAlternateKeyMask && (omod&NSAlternateKeyMask)==0) - keystroke(Kalt); - break; - - default: - panic("getkey: unexpected event type"); - } - omod = m; -} - -/* - * Devdraw does not use NSTrackingArea, that often - * forgets to update the cursor on entering and on - * leaving the area, and that sometimes stops sending - * us MouseMove events, at least on OS X Lion. - */ -static void -updatecursor(void) -{ - NSCursor *c; - int isdown, isinside; - - isinside = NSPointInRect(in.mpos, [win.content bounds]); - isdown = (in.mbuttons || in.kbuttons); - - if(win.cursor && (isinside || isdown)) - c = win.cursor; - else if(isinside && usebigarrow) - c = in.bigarrow; - else - c = [NSCursor arrowCursor]; - [c set]; - - /* - * Without this trick, we can come back from the dock - * with a resize cursor. - */ - if(OSX_VERSION >= 100700) - [NSCursor unhide]; -} - -static void -acceptresizing(int set) -{ - uint old, style; - - old = [WIN styleMask]; - - if((old | NSResizableWindowMask) != Winstyle) - return; /* when entering new fullscreen */ - - if(set) - style = Winstyle; - else - style = Winstyle & ~NSResizableWindowMask; - - if(style != old) - [WIN setStyleMask:style]; -} - -static void -getmousepos(void) -{ - NSPoint p, q; - - p = [WIN mouseLocationOutsideOfEventStream]; - q = [win.content convertPoint:p fromView:nil]; - - /* q is in point coordinates. in.mpos is in pixels. */ - q = scalepoint(q, win.topixelscale); - - in.mpos.x = round(q.x); - in.mpos.y = round(q.y); - - updatecursor(); - - if(win.isnfs || win.isofs) - hidebars(1); - else if(OSX_VERSION>=100700 && [WIN inLiveResize]==0){ - if(p.x<12 && p.y<12 && p.x>2 && p.y>2) - acceptresizing(0); - else - acceptresizing(1); - } -} - -static void -getmouse(NSEvent *e) -{ - float d; - int b, m; - - if([WIN isKeyWindow] == 0) - return; - - getmousepos(); - - switch([e type]){ - case NSLeftMouseDown: - case NSLeftMouseUp: - case NSOtherMouseDown: - case NSOtherMouseUp: - case NSRightMouseDown: - case NSRightMouseUp: - b = [NSEvent pressedMouseButtons]; - b = b&~6 | (b&4)>>1 | (b&2)<<1; - b = mouseswap(b); - - if(b == 1){ - m = [e modifierFlags]; - if(m & NSAlternateKeyMask){ - abortcompose(); - b = 2; - }else - if(m & NSCommandKeyMask) - b = 4; - } - in.mbuttons = b; - break; - - case NSScrollWheel: -#if OSX_VERSION >= 100700 - d = [e scrollingDeltaY]; -#else - d = [e deltaY]; -#endif - if(d>0) - in.mscroll = 8; - else - if(d<0) - in.mscroll = 16; - break; - - case NSMouseMoved: - case NSLeftMouseDragged: - case NSRightMouseDragged: - case NSOtherMouseDragged: - break; - - default: - panic("getmouse: unexpected event type"); - } - sendmouse(); -} - -#define Minpinch 0.02 - -static void -getgesture(NSEvent *e) -{ - switch([e type]){ - case NSEventTypeMagnify: - if(fabs([e magnification]) > Minpinch) - togglefs(); - break; - } -} - -static void sendclick(int); - -static uint -msec(void) -{ - return nsec()/1000000; -} - -static void -gettouch(NSEvent *e, int type) -{ - static int tapping; - static uint taptime; - NSSet *set; - int p; - - switch(type){ - case NSTouchPhaseBegan: - p = NSTouchPhaseTouching; - set = [e touchesMatchingPhase:p inView:nil]; - if(set.count == 3){ - tapping = 1; - taptime = msec(); - }else - if(set.count > 3) - tapping = 0; - break; - - case NSTouchPhaseMoved: - tapping = 0; - break; - - case NSTouchPhaseEnded: - p = NSTouchPhaseTouching; - set = [e touchesMatchingPhase:p inView:nil]; - if(set.count == 0){ - if(tapping && msec()-taptime<400) - sendclick(2); - tapping = 0; - } - break; - - case NSTouchPhaseCancelled: - break; - - default: - panic("gettouch: unexpected event type"); - } -} - -static void -sendclick(int b) -{ - in.mbuttons = b; - sendmouse(); - in.mbuttons = 0; - sendmouse(); -} - -static void -sendmouse(void) -{ - NSSize size; - int b; - - size = winsizepixels(); - mouserect = Rect(0, 0, size.width, size.height); - - b = in.kbuttons | in.mbuttons | in.mscroll; - mousetrack(in.mpos.x, in.mpos.y, b, msec()); - in.mscroll = 0; -} - -/* - * |p| is in pixels. - */ -void -setmouse(Point p) -{ - NSPoint q; - NSRect r; - - if([NSApp isActive]==0 && in.willactivate==0) - return; - - if([WIN inLiveResize]) - return; - - in.mpos = scalepoint(NSMakePoint(p.x, p.y), win.topointscale); // race condition - - q = [win.content convertPoint:in.mpos toView:nil]; - q = [WIN convertRectToScreen:NSMakeRect(q.x, q.y, 0, 0)].origin; - - r = [[[NSScreen screens] objectAtIndex:0] frame]; - q.y = r.size.height - q.y; /* Quartz is top-left-based here */ - - CGWarpMouseCursorPosition(NSPointToCGPoint(q)); - CGAssociateMouseAndMouseCursorPosition(true); -} - -/* - * |r| is in points. - */ -static void -followzoombutton(NSRect r) -{ - NSRect wr; - Point p; - NSPoint pt; - - wr = [WIN frame]; - wr.origin.y += wr.size.height; - r.origin.y += r.size.height; - - getmousepos(); - pt.x = in.mpos.x; - pt.y = in.mpos.y; - pt = scalepoint(pt, win.topointscale); - pt.x = (r.origin.x - wr.origin.x) + pt.x; - pt.y = -(r.origin.y - wr.origin.y) + pt.y; - pt = scalepoint(pt, win.topixelscale); - - p.x = pt.x; - p.y = pt.y; - - setmouse(p); -} - -static void -togglefs(void) -{ - uint opt, tmp; - -#if OSX_VERSION >= 100700 - NSScreen *s, *s0; - - s = [WIN screen]; - s0 = [[NSScreen screens] objectAtIndex:0]; - - if((s==s0 && useoldfullscreen==0) || win.isnfs) { - [WIN toggleFullScreen:nil]; - return; - } -#endif - [win.content retain]; - [WIN orderOut:nil]; - [WIN setContentView:nil]; - - win.isofs = ! win.isofs; - hidebars(win.isofs); - - /* - * If we move the window from one space to another, - * ofs[0] and ofs[1] can be on different spaces. - * This "setCollectionBehavior" trick moves the - * window to the active space. - */ - opt = [WIN collectionBehavior]; - tmp = opt | NSWindowCollectionBehaviorCanJoinAllSpaces; - [WIN setContentView:win.content]; - [WIN setCollectionBehavior:tmp]; - [WIN makeKeyAndOrderFront:nil]; - [WIN setCollectionBehavior:opt]; - [win.content release]; -} - -enum -{ - Autohiddenbars = NSApplicationPresentationAutoHideDock - | NSApplicationPresentationAutoHideMenuBar, - - Hiddenbars = NSApplicationPresentationHideDock - | NSApplicationPresentationHideMenuBar, -}; - -static void -hidebars(int set) -{ - NSScreen *s,*s0; - uint old, opt; - - s = [WIN screen]; - s0 = [[NSScreen screens] objectAtIndex:0]; - old = [NSApp presentationOptions]; - -#if OSX_VERSION >= 100700 - /* This bit can get lost, resulting in dreadful bugs. */ - if(win.isnfs) - old |= NSApplicationPresentationFullScreen; -#endif - - if(set && s==s0) - opt = (old & ~Autohiddenbars) | Hiddenbars; - else - opt = old & ~(Autohiddenbars | Hiddenbars); - - if(opt != old) - [NSApp setPresentationOptions:opt]; -} - -static void -makemenu(void) -{ - NSMenu *m; - NSMenuItem *i0,*i1; - - m = [NSMenu new]; - i0 = [m addItemWithTitle:@"app" action:NULL keyEquivalent:@""]; - i1 = [m addItemWithTitle:@"help" action:NULL keyEquivalent:@""]; - [NSApp setMainMenu:m]; - [m release]; - - m = [[NSMenu alloc] initWithTitle:@"app"]; - [m addItemWithTitle:@"Full Screen" - action:@selector(calltogglefs:) - keyEquivalent:@"f"]; - [m addItemWithTitle:@"Hide" - action:@selector(hide:) - keyEquivalent:@"h"]; - [m addItemWithTitle:@"Quit" - action:@selector(terminate:) - keyEquivalent:@"q"]; - [i0 setSubmenu:m]; - [m release]; - - m = [[NSMenu alloc] initWithTitle:@"help"]; - [m addItemWithTitle:@"Plumb devdraw(1)" - action:@selector(plumbmanual:) - keyEquivalent:@""]; - [i1 setSubmenu:m]; - [m release]; -} - -// FIXME: Introduce a high-resolution Glenda image. -static void -makeicon(void) -{ - NSData *d; - NSImage *i; - - d = [[NSData alloc] - initWithBytes:glenda_png - length:(sizeof glenda_png)]; - - i = [[NSImage alloc] initWithData:d]; - [NSApp setApplicationIconImage:i]; - [[NSApp dockTile] display]; - [i release]; - [d release]; -} - -QLock snarfl; - -char* -getsnarf(void) -{ - NSPasteboard *pb; - NSString *s; - - pb = [NSPasteboard generalPasteboard]; - - qlock(&snarfl); - s = [pb stringForType:NSPasteboardTypeString]; - qunlock(&snarfl); - - if(s) - return strdup((char*)[s UTF8String]); - else - return nil; -} - -void -putsnarf(char *s) -{ - NSArray *t; - NSPasteboard *pb; - NSString *str; - - if(strlen(s) >= SnarfSize) - return; - - t = [NSArray arrayWithObject:NSPasteboardTypeString]; - pb = [NSPasteboard generalPasteboard]; - str = [[NSString alloc] initWithUTF8String:s]; - - qlock(&snarfl); - [pb declareTypes:t owner:nil]; - [pb setString:str forType:NSPasteboardTypeString]; - qunlock(&snarfl); - - [str release]; -} - -void -kicklabel(char *label) -{ - if(label == nil) - return; - - [appdelegate - performSelectorOnMainThread:@selector(callkicklabel0:) - withObject:[NSValue valueWithPointer:label] - waitUntilDone:YES]; -} - -static void -kicklabel0(char *label) { - NSString *s; - - s = [[NSString alloc] initWithUTF8String:label]; - [win.ofs[0] setTitle:s]; - [win.ofs[1] setTitle:s]; - [[NSApp dockTile] setBadgeLabel:s]; - [s release]; -} - -void -setcursor(Cursor *c, Cursor2 *c2) -{ - USED(c2); - - /* - * No cursor change unless in main thread. - */ - [appdelegate - performSelectorOnMainThread:@selector(callsetcursor0:) - withObject:[NSValue valueWithPointer:c] - waitUntilDone:YES]; -} - -static void -setcursor0(Cursor *c) -{ - NSCursor *d; - - d = win.cursor; - - if(c) - win.cursor = makecursor(c); - else - win.cursor = nil; - - updatecursor(); - - if(d) - [d release]; -} - -/* - * Cursors will be scaled on retina display. - */ -static NSCursor* -makecursor(Cursor *c) -{ - NSBitmapImageRep *r; - NSCursor *d; - NSImage *i; - NSPoint p; - int b; - uchar *plane[5]; - - r = [[NSBitmapImageRep alloc] - initWithBitmapDataPlanes:nil - pixelsWide:16 - pixelsHigh:16 - bitsPerSample:1 - samplesPerPixel:2 - hasAlpha:YES - isPlanar:YES - colorSpaceName:NSDeviceWhiteColorSpace - bytesPerRow:2 - bitsPerPixel:1]; - - [r getBitmapDataPlanes:plane]; - - for(b=0; b<2*16; b++){ - plane[0][b] = ~c->set[b]; - plane[1][b] = c->clr[b]; - } - p = NSMakePoint(-c->offset.x, -c->offset.y); - i = [NSImage new]; - [i addRepresentation:r]; - [r release]; - - d = [[NSCursor alloc] initWithImage:i hotSpot:p]; - [i release]; - return d; -} - -void -topwin(void) -{ - [WIN performSelectorOnMainThread: - @selector(makeKeyAndOrderFront:) - withObject:nil - waitUntilDone:NO]; - - in.willactivate = 1; - [NSApp activateIgnoringOtherApps:YES]; -} - -static NSSize -winsizepoints() -{ - return [win.content bounds].size; -} - -static NSSize -winsizepixels() -{ -#if OSX_VERSION >= 100700 - if (OSX_VERSION >= 100700 && devdrawretina) - return [win.content convertSizeToBacking: winsizepoints()]; - else -#endif - return winsizepoints(); -} - -static NSRect -scalerect(NSRect r, CGFloat scale) -{ - r.origin.x *= scale; - r.origin.y *= scale; - r.size.width *= scale; - r.size.height *= scale; - return r; -} - -/* - * Expands rectangle |r|'s bounds to more inclusive integer bounds to - * eliminate 1 pixel gaps. - */ -static NSRect -dilate(NSRect r) -{ - if(win.topixelscale > 1.0f){ - r.origin.x = floorf(r.origin.x); - r.origin.y = floorf(r.origin.y); - r.size.width = ceilf(r.size.width + 0.5); - r.size.height = ceilf(r.size.height + 0.5); - } - return r; -} - -static NSPoint -scalepoint(NSPoint pt, CGFloat scale) -{ - pt.x *= scale; - pt.y *= scale; - return pt; -} - -static void -setprocname(const char *s) -{ - CFStringRef process_name; - - process_name = CFStringCreateWithBytes(nil, (uchar*)s, strlen(s), kCFStringEncodingUTF8, false); - - // Adapted from Chrome's mac_util.mm. - // http://src.chromium.org/viewvc/chrome/trunk/src/base/mac/mac_util.mm - // - // Copyright (c) 2012 The Chromium Authors. All rights reserved. - // - // Redistribution and use in source and binary forms, with or without - // modification, are permitted provided that the following conditions are - // met: - // - // * Redistributions of source code must retain the above copyright - // notice, this list of conditions and the following disclaimer. - // * Redistributions in binary form must reproduce the above - // copyright notice, this list of conditions and the following disclaimer - // in the documentation and/or other materials provided with the - // distribution. - // * Neither the name of Google Inc. nor the names of its - // contributors may be used to endorse or promote products derived from - // this software without specific prior written permission. - // - // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - // Warning: here be dragons! This is SPI reverse-engineered from WebKit's - // plugin host, and could break at any time (although realistically it's only - // likely to break in a new major release). - // When 10.7 is available, check that this still works, and update this - // comment for 10.8. - - // Private CFType used in these LaunchServices calls. - typedef CFTypeRef PrivateLSASN; - typedef PrivateLSASN (*LSGetCurrentApplicationASNType)(); - typedef OSStatus (*LSSetApplicationInformationItemType)(int, PrivateLSASN, - CFStringRef, - CFStringRef, - CFDictionaryRef*); - - static LSGetCurrentApplicationASNType ls_get_current_application_asn_func = - NULL; - static LSSetApplicationInformationItemType - ls_set_application_information_item_func = NULL; - static CFStringRef ls_display_name_key = NULL; - - static bool did_symbol_lookup = false; - if (!did_symbol_lookup) { - did_symbol_lookup = true; - CFBundleRef launch_services_bundle = - CFBundleGetBundleWithIdentifier(CFSTR("com.apple.LaunchServices")); - if (!launch_services_bundle) { - fprint(2, "Failed to look up LaunchServices bundle\n"); - return; - } - - ls_get_current_application_asn_func = - (LSGetCurrentApplicationASNType)( - CFBundleGetFunctionPointerForName( - launch_services_bundle, CFSTR("_LSGetCurrentApplicationASN"))); - if (!ls_get_current_application_asn_func) - fprint(2, "Could not find _LSGetCurrentApplicationASN\n"); - - ls_set_application_information_item_func = - (LSSetApplicationInformationItemType)( - CFBundleGetFunctionPointerForName( - launch_services_bundle, - CFSTR("_LSSetApplicationInformationItem"))); - if (!ls_set_application_information_item_func) - fprint(2, "Could not find _LSSetApplicationInformationItem\n"); - - CFStringRef* key_pointer = (CFStringRef*)( - CFBundleGetDataPointerForName(launch_services_bundle, - CFSTR("_kLSDisplayNameKey"))); - ls_display_name_key = key_pointer ? *key_pointer : NULL; - if (!ls_display_name_key) - fprint(2, "Could not find _kLSDisplayNameKey\n"); - - // Internally, this call relies on the Mach ports that are started up by the - // Carbon Process Manager. In debug builds this usually happens due to how - // the logging layers are started up; but in release, it isn't started in as - // much of a defined order. So if the symbols had to be loaded, go ahead - // and force a call to make sure the manager has been initialized and hence - // the ports are opened. - ProcessSerialNumber psn; - GetCurrentProcess(&psn); - } - if (!ls_get_current_application_asn_func || - !ls_set_application_information_item_func || - !ls_display_name_key) { - return; - } - - PrivateLSASN asn = ls_get_current_application_asn_func(); - // Constant used by WebKit; what exactly it means is unknown. - const int magic_session_constant = -2; - OSErr err = - ls_set_application_information_item_func(magic_session_constant, asn, - ls_display_name_key, - process_name, - NULL /* optional out param */); - if(err != noErr) - fprint(2, "Call to set process name failed\n"); -} - -void -resizewindow(Rectangle r) -{ - USED(r); -} diff --git a/src/cmd/devdraw/cocoa-srv.c b/src/cmd/devdraw/cocoa-srv.c deleted file mode 100644 index c1cf5983..00000000 --- a/src/cmd/devdraw/cocoa-srv.c +++ /dev/null @@ -1,427 +0,0 @@ -/* - * Window system protocol server. - */ - -#include <u.h> -#include <libc.h> -#include "cocoa-thread.h" -#include <draw.h> -#include <memdraw.h> -#include <keyboard.h> -#include <mouse.h> -#include <cursor.h> -#include <drawfcall.h> -#include "cocoa-screen.h" -#include "devdraw.h" - -typedef struct Kbdbuf Kbdbuf; -typedef struct Mousebuf Mousebuf; -typedef struct Fdbuf Fdbuf; -typedef struct Tagbuf Tagbuf; - -struct Kbdbuf -{ - Rune r[256]; - int ri; - int wi; - int stall; -}; - -struct Mousebuf -{ - Mouse m[256]; - Mouse last; - int ri; - int wi; - int stall; -}; - -struct Tagbuf -{ - int t[256]; - int ri; - int wi; -}; - -Kbdbuf kbd; -Mousebuf mouse; -Tagbuf kbdtags; -Tagbuf mousetags; - -void runmsg(Wsysmsg*); -void replymsg(Wsysmsg*); -void matchkbd(void); -void matchmouse(void); - - -QLock lk; -void -zlock(void) -{ - qlock(&lk); -} - -void -zunlock(void) -{ - qunlock(&lk); -} - -int trace = 0; - -void -servep9p(void) -{ - uchar buf[4], *mbuf; - int nmbuf, n, nn; - Wsysmsg m; - - fmtinstall('W', drawfcallfmt); - - mbuf = nil; - nmbuf = 0; - while((n = read(3, buf, 4)) == 4){ - GET(buf, n); - if(n > nmbuf){ - free(mbuf); - mbuf = malloc(4+n); - if(mbuf == nil) - sysfatal("malloc: %r"); - nmbuf = n; - } - memmove(mbuf, buf, 4); - nn = readn(3, mbuf+4, n-4); - if(nn != n-4) - sysfatal("eof during message"); - - /* pick off messages one by one */ - if(convM2W(mbuf, nn+4, &m) <= 0) - sysfatal("cannot convert message"); - if(trace) fprint(2, "%ud [%d] <- %W\n", nsec()/1000000, threadid(), &m); - runmsg(&m); - } -} - -void -replyerror(Wsysmsg *m) -{ - char err[256]; - - rerrstr(err, sizeof err); - m->type = Rerror; - m->error = err; - replymsg(m); -} - -/* - * Handle a single wsysmsg. - * Might queue for later (kbd, mouse read) - */ -void -runmsg(Wsysmsg *m) -{ - static uchar buf[65536]; - int n; - Memimage *i; - - switch(m->type){ - case Tinit: - memimageinit(); - i = attachscreen(m->label, m->winsize); - _initdisplaymemimage(i); - replymsg(m); - break; - - case Trdmouse: - zlock(); - mousetags.t[mousetags.wi++] = m->tag; - if(mousetags.wi == nelem(mousetags.t)) - mousetags.wi = 0; - if(mousetags.wi == mousetags.ri) - sysfatal("too many queued mouse reads"); - mouse.stall = 0; - matchmouse(); - zunlock(); - break; - - case Trdkbd: - zlock(); - kbdtags.t[kbdtags.wi++] = m->tag; - if(kbdtags.wi == nelem(kbdtags.t)) - kbdtags.wi = 0; - if(kbdtags.wi == kbdtags.ri) - sysfatal("too many queued keyboard reads"); - kbd.stall = 0; - matchkbd(); - zunlock(); - break; - - case Tmoveto: - setmouse(m->mouse.xy); - replymsg(m); - break; - - case Tcursor: - if(m->arrowcursor) - setcursor(nil, nil); - else - setcursor(&m->cursor, nil); - replymsg(m); - break; - - case Tcursor2: - if(m->arrowcursor) - setcursor(nil, nil); - else - setcursor(&m->cursor, &m->cursor2); - replymsg(m); - break; - - case Tbouncemouse: - // _xbouncemouse(&m->mouse); - replymsg(m); - break; - - case Tlabel: - kicklabel(m->label); - replymsg(m); - break; - - case Trdsnarf: - m->snarf = getsnarf(); - replymsg(m); - free(m->snarf); - break; - - case Twrsnarf: - putsnarf(m->snarf); - replymsg(m); - break; - - case Trddraw: - zlock(); - n = m->count; - if(n > sizeof buf) - n = sizeof buf; - n = _drawmsgread(buf, n); - if(n < 0) - replyerror(m); - else{ - m->count = n; - m->data = buf; - replymsg(m); - } - zunlock(); - break; - - case Twrdraw: - zlock(); - if(_drawmsgwrite(m->data, m->count) < 0) - replyerror(m); - else - replymsg(m); - zunlock(); - break; - - case Ttop: - topwin(); - replymsg(m); - break; - - case Tresize: - resizewindow(m->rect); - replymsg(m); - break; - } -} - -/* - * Reply to m. - */ -QLock replylock; -void -replymsg(Wsysmsg *m) -{ - int n; - static uchar *mbuf; - static int nmbuf; - - /* T -> R msg */ - if(m->type%2 == 0) - m->type++; - - if(trace) fprint(2, "%ud [%d] -> %W\n", nsec()/1000000, threadid(), m); - /* copy to output buffer */ - n = sizeW2M(m); - - qlock(&replylock); - if(n > nmbuf){ - free(mbuf); - mbuf = malloc(n); - if(mbuf == nil) - sysfatal("out of memory"); - nmbuf = n; - } - convW2M(m, mbuf, n); - if(write(4, mbuf, n) != n) - sysfatal("write: %r"); - qunlock(&replylock); -} - -/* - * Match queued kbd reads with queued kbd characters. - */ -void -matchkbd(void) -{ - Wsysmsg m; - - if(kbd.stall) - return; - while(kbd.ri != kbd.wi && kbdtags.ri != kbdtags.wi){ - m.type = Rrdkbd; - m.tag = kbdtags.t[kbdtags.ri++]; - if(kbdtags.ri == nelem(kbdtags.t)) - kbdtags.ri = 0; - m.rune = kbd.r[kbd.ri++]; - if(kbd.ri == nelem(kbd.r)) - kbd.ri = 0; - replymsg(&m); - } -} - -/* - * Match queued mouse reads with queued mouse events. - */ -void -matchmouse(void) -{ - Wsysmsg m; - - while(mouse.ri != mouse.wi && mousetags.ri != mousetags.wi){ - m.type = Rrdmouse; - m.tag = mousetags.t[mousetags.ri++]; - if(mousetags.ri == nelem(mousetags.t)) - mousetags.ri = 0; - m.mouse = mouse.m[mouse.ri]; - m.resized = mouseresized; - mouseresized = 0; - /* - if(m.resized) - fprint(2, "sending resize\n"); - */ - mouse.ri++; - if(mouse.ri == nelem(mouse.m)) - mouse.ri = 0; - replymsg(&m); - } -} - -void -mousetrack(int x, int y, int b, uint ms) -{ - Mouse *m; - - if(x < mouserect.min.x) - x = mouserect.min.x; - if(x > mouserect.max.x) - x = mouserect.max.x; - if(y < mouserect.min.y) - y = mouserect.min.y; - if(y > mouserect.max.y) - y = mouserect.max.y; - - zlock(); - // If reader has stopped reading, don't bother. - // If reader is completely caught up, definitely queue. - // Otherwise, queue only button change events. - if(!mouse.stall) - if(mouse.wi == mouse.ri || mouse.last.buttons != b){ - m = &mouse.last; - m->xy.x = x; - m->xy.y = y; - m->buttons = b; - m->msec = ms; - - mouse.m[mouse.wi] = *m; - if(++mouse.wi == nelem(mouse.m)) - mouse.wi = 0; - if(mouse.wi == mouse.ri){ - mouse.stall = 1; - mouse.ri = 0; - mouse.wi = 1; - mouse.m[0] = *m; - } - matchmouse(); - } - zunlock(); -} - -void -kputc(int c) -{ - zlock(); - kbd.r[kbd.wi++] = c; - if(kbd.wi == nelem(kbd.r)) - kbd.wi = 0; - if(kbd.ri == kbd.wi) - kbd.stall = 1; - matchkbd(); - zunlock(); -} - -static int alting; - -void -abortcompose(void) -{ - if(alting) - keystroke(Kalt); -} - -void -keystroke(int c) -{ - static Rune k[10]; - static int nk; - int i; - - if(c == Kalt){ - alting = !alting; - nk = 0; - return; - } - if(c == Kcmd+'r') { - if(forcedpi) - forcedpi = 0; - else if(displaydpi >= 200) - forcedpi = 100; - else - forcedpi = 225; - resizeimg(); - return; - } - if(!alting){ - kputc(c); - return; - } - if(nk >= nelem(k)) // should not happen - nk = 0; - k[nk++] = c; - c = _latin1(k, nk); - if(c > 0){ - alting = 0; - kputc(c); - nk = 0; - return; - } - if(c == -1){ - alting = 0; - for(i=0; i<nk; i++) - kputc(k[i]); - nk = 0; - return; - } - // need more input - return; -} diff --git a/src/cmd/devdraw/cocoa-thread.c b/src/cmd/devdraw/cocoa-thread.c deleted file mode 100644 index 92b92d2c..00000000 --- a/src/cmd/devdraw/cocoa-thread.c +++ /dev/null @@ -1,35 +0,0 @@ -#include <u.h> -#include <libc.h> -#include "cocoa-thread.h" - -#ifndef TRY_LIBTHREAD - -static pthread_mutex_t initlock = PTHREAD_MUTEX_INITIALIZER; - -void -qlock(QLock *q) -{ - if(q->init == 0){ - pthread_mutex_lock(&initlock); - if(q->init == 0){ - pthread_mutex_init(&q->m, nil); - q->init = 1; - } - pthread_mutex_unlock(&initlock); - } - pthread_mutex_lock(&q->m); -} - -void -qunlock(QLock *q) -{ - pthread_mutex_unlock(&q->m); -} - -int -threadid(void) -{ - return pthread_mach_thread_np(pthread_self()); -} - -#endif diff --git a/src/cmd/devdraw/cocoa-thread.h b/src/cmd/devdraw/cocoa-thread.h deleted file mode 100644 index d5793f0a..00000000 --- a/src/cmd/devdraw/cocoa-thread.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * I am too ignorant to know if Cocoa and Libthread - * can coexist: if I try to include thread.h, now - * that Devdraw uses Cocoa's threads (and timers), it - * crashes immediately; when Devdraw was using - * proccreate(), it could run a little while before to - * crash; the origin of those crashes is hard to - * ascertain, because other programs using Libthread - * (such as 9term, Acme, Plumber, and Sam) currently - * don't run when compiled with Xcode 4.1. - */ -//#define TRY_LIBTHREAD - -#ifdef TRY_LIBTHREAD - #include <thread.h> -#else - #define QLock DQLock - #define qlock dqlock - #define qunlock dqunlock - #define threadexitsall exits - #define threadmain main - - typedef struct QLock QLock; - - struct QLock - { - int init; - pthread_mutex_t m; - }; - - void qlock(QLock*); - void qunlock(QLock*); - int threadid(void); -#endif diff --git a/src/cmd/devdraw/devdraw.c b/src/cmd/devdraw/devdraw.c index 7f0bff21..086574ef 100644 --- a/src/cmd/devdraw/devdraw.c +++ b/src/cmd/devdraw/devdraw.c @@ -8,181 +8,60 @@ #include <draw.h> #include <memdraw.h> #include <memlayer.h> +#include <mouse.h> +#include <cursor.h> +#include <keyboard.h> +#include <drawfcall.h> #include "devdraw.h" -extern void _flushmemscreen(Rectangle); -int forcedpi = 0; -int displaydpi = 100; - -#define NHASH (1<<5) -#define HASHMASK (NHASH-1) - -typedef struct Client Client; -typedef struct Draw Draw; -typedef struct DImage DImage; -typedef struct DScreen DScreen; -typedef struct CScreen CScreen; -typedef struct FChar FChar; -typedef struct Refresh Refresh; -typedef struct Refx Refx; -typedef struct DName DName; - -struct Draw -{ - QLock lk; - int clientid; - int nclient; - Client* client[1]; - int nname; - DName* name; - int vers; - int softscreen; -}; - -struct Client -{ - /*Ref r;*/ - DImage* dimage[NHASH]; - CScreen* cscreen; - Refresh* refresh; - Rendez refrend; - uchar* readdata; - int nreaddata; - int busy; - int clientid; - int slot; - int refreshme; - int infoid; - int op; -}; - -struct Refresh -{ - DImage* dimage; - Rectangle r; - Refresh* next; -}; - -struct Refx -{ - Client* client; - DImage* dimage; -}; - -struct DName -{ - char *name; - Client *client; - DImage* dimage; - int vers; -}; - -struct FChar -{ - int minx; /* left edge of bits */ - int maxx; /* right edge of bits */ - uchar miny; /* first non-zero scan-line */ - uchar maxy; /* last non-zero scan-line + 1 */ - schar left; /* offset of baseline */ - uchar width; /* width of baseline */ -}; - -/* - * Reference counts in DImages: - * one per open by original client - * one per screen image or fill - * one per image derived from this one by name - */ -struct DImage -{ - int id; - int ref; - char *name; - int vers; - Memimage* image; - int ascent; - int nfchar; - FChar* fchar; - DScreen* dscreen; /* 0 if not a window */ - DImage* fromname; /* image this one is derived from, by name */ - DImage* next; -}; - -struct CScreen -{ - DScreen* dscreen; - CScreen* next; -}; - -struct DScreen -{ - int id; - int public; - int ref; - DImage *dimage; - DImage *dfill; - Memscreen* screen; - Client* owner; - DScreen* next; -}; - -static Draw sdraw; -static Client *client0; -static Memimage *screenimage; -static Rectangle flushrect; -static int waste; -static DScreen* dscreen; static int drawuninstall(Client*, int); static Memimage* drawinstall(Client*, int, Memimage*, DScreen*); -static void drawfreedimage(DImage*); +static void drawfreedimage(Client*, DImage*); void -_initdisplaymemimage(Memimage *m) +draw_initdisplaymemimage(Client *c, Memimage *m) { - screenimage = m; + c->screenimage = m; m->screenref = 1; - client0 = mallocz(sizeof(Client), 1); - if(client0 == nil){ - fprint(2, "initdraw: allocating client0: out of memory"); - abort(); - } - client0->slot = 0; - client0->clientid = ++sdraw.clientid; - client0->op = SoverD; - sdraw.client[0] = client0; - sdraw.nclient = 1; - sdraw.softscreen = 1; + c->slot = 0; + c->clientid = 1; + c->op = SoverD; } +// gfx_replacescreenimage replaces c's screen image with m. +// It is called by the host driver on the main host thread. void -_drawreplacescreenimage(Memimage *m) +gfx_replacescreenimage(Client *c, Memimage *m) { /* * Replace the screen image because the screen * was resized. - * + * * In theory there should only be one reference * to the current screen image, and that's through * client0's image 0, installed a few lines above. - * Once the client drops the image, the underlying backing + * Once the client drops the image, the underlying backing * store freed properly. The client is being notified * about the resize through external means, so all we * need to do is this assignment. */ Memimage *om; - qlock(&sdraw.lk); - om = screenimage; - screenimage = m; + qlock(&c->drawlk); + om = c->screenimage; + c->screenimage = m; m->screenref = 1; if(om && --om->screenref == 0){ _freememimage(om); } - qunlock(&sdraw.lk); + qunlock(&c->drawlk); + + qlock(&c->eventlk); + c->mouse.resized = 1; + qunlock(&c->eventlk); } -static -void +static void drawrefreshscreen(DImage *l, Client *client) { while(l != nil && l->dscreen == nil) @@ -191,8 +70,7 @@ drawrefreshscreen(DImage *l, Client *client) l->dscreen->owner->refreshme = 1; } -static -void +static void drawrefresh(Memimage *m, Rectangle r, void *v) { Refx *x; @@ -222,57 +100,65 @@ drawrefresh(Memimage *m, Rectangle r, void *v) } static void -addflush(Rectangle r) +addflush(Client *c, Rectangle r) { int abb, ar, anbb; - Rectangle nbb; + Rectangle nbb, fr; - if(sdraw.softscreen==0 || !rectclip(&r, screenimage->r)) + if(/*sdraw.softscreen==0 ||*/ !rectclip(&r, c->screenimage->r)) return; - if(flushrect.min.x >= flushrect.max.x){ - flushrect = r; - waste = 0; + if(c->flushrect.min.x >= c->flushrect.max.x){ + c->flushrect = r; + c->waste = 0; return; } - nbb = flushrect; + nbb = c->flushrect; combinerect(&nbb, r); ar = Dx(r)*Dy(r); - abb = Dx(flushrect)*Dy(flushrect); + abb = Dx(c->flushrect)*Dy(c->flushrect); anbb = Dx(nbb)*Dy(nbb); /* * Area of new waste is area of new bb minus area of old bb, * less the area of the new segment, which we assume is not waste. * This could be negative, but that's OK. */ - waste += anbb-abb - ar; - if(waste < 0) - waste = 0; + c->waste += anbb-abb - ar; + if(c->waste < 0) + c->waste = 0; /* * absorb if: * total area is small * waste is less than half total area * rectangles touch */ - if(anbb<=1024 || waste*2<anbb || rectXrect(flushrect, r)){ - flushrect = nbb; + if(anbb<=1024 || c->waste*2<anbb || rectXrect(c->flushrect, r)){ + c->flushrect = nbb; return; } /* emit current state */ - if(flushrect.min.x < flushrect.max.x) - _flushmemscreen(flushrect); - flushrect = r; - waste = 0; + fr = c->flushrect; + c->flushrect = r; + c->waste = 0; + if(fr.min.x < fr.max.x) { + // Unlock drawlk because rpc_flush may want to run on gfx thread, + // and gfx thread might be blocked on drawlk trying to install a new screen + // during a resize. + rpc_gfxdrawunlock(); + qunlock(&c->drawlk); + rpc_flush(c, fr); + qlock(&c->drawlk); + rpc_gfxdrawlock(); + } } -static -void -dstflush(int dstid, Memimage *dst, Rectangle r) +static void +dstflush(Client *c, int dstid, Memimage *dst, Rectangle r) { Memlayer *l; if(dstid == 0){ - combinerect(&flushrect, r); + combinerect(&c->flushrect, r); return; } /* how can this happen? -rsc, dec 12 2002 */ @@ -284,25 +170,34 @@ dstflush(int dstid, Memimage *dst, Rectangle r) if(l == nil) return; do{ - if(l->screen->image->data != screenimage->data) + if(l->screen->image->data != c->screenimage->data) return; r = rectaddpt(r, l->delta); l = l->screen->image->layer; }while(l); - addflush(r); + addflush(c, r); } -static -void -drawflush(void) +static void +drawflush(Client *c) { - if(flushrect.min.x < flushrect.max.x) - _flushmemscreen(flushrect); - flushrect = Rect(10000, 10000, -10000, -10000); + Rectangle r; + + r = c->flushrect; + c->flushrect = Rect(10000, 10000, -10000, -10000); + if(r.min.x < r.max.x) { + // Unlock drawlk because rpc_flush may want to run on gfx thread, + // and gfx thread might be blocked on drawlk trying to install a new screen + // during a resize. + rpc_gfxdrawunlock(); + qunlock(&c->drawlk); + rpc_flush(c, r); + qlock(&c->drawlk); + rpc_gfxdrawlock(); + } } -static -int +static int drawcmp(char *a, char *b, int n) { if(strlen(a) != n) @@ -310,41 +205,38 @@ drawcmp(char *a, char *b, int n) return memcmp(a, b, n); } -static -DName* -drawlookupname(int n, char *str) +static DName* +drawlookupname(Client *client, int n, char *str) { DName *name, *ename; - name = sdraw.name; - ename = &name[sdraw.nname]; + name = client->name; + ename = &name[client->nname]; for(; name<ename; name++) if(drawcmp(name->name, str, n) == 0) return name; return 0; } -static -int -drawgoodname(DImage *d) +static int +drawgoodname(Client *client, DImage *d) { DName *n; /* if window, validate the screen's own images */ if(d->dscreen) - if(drawgoodname(d->dscreen->dimage) == 0 - || drawgoodname(d->dscreen->dfill) == 0) + if(drawgoodname(client, d->dscreen->dimage) == 0 + || drawgoodname(client, d->dscreen->dfill) == 0) return 0; if(d->name == nil) return 1; - n = drawlookupname(strlen(d->name), d->name); + n = drawlookupname(client, strlen(d->name), d->name); if(n==nil || n->vers!=d->vers) return 0; return 1; } -static -DImage* +static DImage* drawlookup(Client *client, int id, int checkname) { DImage *d; @@ -356,7 +248,7 @@ drawlookup(Client *client, int id, int checkname) * BUG: should error out but too hard. * Return 0 instead. */ - if(checkname && !drawgoodname(d)) + if(checkname && !drawgoodname(client, d)) return 0; return d; } @@ -365,13 +257,12 @@ drawlookup(Client *client, int id, int checkname) return 0; } -static -DScreen* -drawlookupdscreen(int id) +static DScreen* +drawlookupdscreen(Client *c, int id) { DScreen *s; - s = dscreen; + s = c->dscreen; while(s){ if(s->id == id) return s; @@ -380,8 +271,7 @@ drawlookupdscreen(int id) return 0; } -static -DScreen* +static DScreen* drawlookupscreen(Client *client, int id, CScreen **cs) { CScreen *s; @@ -398,8 +288,7 @@ drawlookupscreen(Client *client, int id, CScreen **cs) return 0; } -static -Memimage* +static Memimage* drawinstall(Client *client, int id, Memimage *i, DScreen *dscreen) { DImage *d; @@ -423,8 +312,7 @@ drawinstall(Client *client, int id, Memimage *i, DScreen *dscreen) return i; } -static -Memscreen* +static Memscreen* drawinstallscreen(Client *client, DScreen *d, int id, DImage *dimage, DImage *dfill, int public) { Memscreen *s; @@ -466,9 +354,9 @@ drawinstallscreen(Client *client, DScreen *d, int id, DImage *dimage, DImage *df d->id = id; d->screen = s; d->public = public; - d->next = dscreen; + d->next = client->dscreen; d->owner = client; - dscreen = d; + client->dscreen = d; } c->dscreen = d; d->ref++; @@ -477,20 +365,18 @@ drawinstallscreen(Client *client, DScreen *d, int id, DImage *dimage, DImage *df return d->screen; } -static -void -drawdelname(DName *name) +static void +drawdelname(Client *client, DName *name) { int i; - i = name-sdraw.name; - memmove(name, name+1, (sdraw.nname-(i+1))*sizeof(DName)); - sdraw.nname--; + i = name-client->name; + memmove(name, name+1, (client->nname-(i+1))*sizeof(DName)); + client->nname--; } -static -void -drawfreedscreen(DScreen *this) +static void +drawfreedscreen(Client *client, DScreen *this) { DScreen *ds, *next; @@ -499,9 +385,9 @@ drawfreedscreen(DScreen *this) fprint(2, "negative ref in drawfreedscreen\n"); if(this->ref > 0) return; - ds = dscreen; + ds = client->dscreen; if(ds == this){ - dscreen = this->next; + client->dscreen = this->next; goto Found; } while(next = ds->next){ /* assign = */ @@ -518,16 +404,15 @@ drawfreedscreen(DScreen *this) Found: if(this->dimage) - drawfreedimage(this->dimage); + drawfreedimage(client, this->dimage); if(this->dfill) - drawfreedimage(this->dfill); + drawfreedimage(client, this->dfill); free(this->screen); free(this); } -static -void -drawfreedimage(DImage *dimage) +static void +drawfreedimage(Client *client, DImage *dimage) { int i; Memimage *l; @@ -540,13 +425,13 @@ drawfreedimage(DImage *dimage) return; /* any names? */ - for(i=0; i<sdraw.nname; ) - if(sdraw.name[i].dimage == dimage) - drawdelname(sdraw.name+i); + for(i=0; i<client->nname; ) + if(client->name[i].dimage == dimage) + drawdelname(client, client->name+i); else i++; if(dimage->fromname){ /* acquired by name; owned by someone else*/ - drawfreedimage(dimage->fromname); + drawfreedimage(client, dimage->fromname); goto Return; } ds = dimage->dscreen; @@ -554,16 +439,16 @@ drawfreedimage(DImage *dimage) dimage->dscreen = nil; /* paranoia */ dimage->image = nil; if(ds){ - if(l->data == screenimage->data) - addflush(l->layer->screenr); + if(l->data == client->screenimage->data) + addflush(client, l->layer->screenr); if(l->layer->refreshfn == drawrefresh) /* else true owner will clean up */ free(l->layer->refreshptr); l->layer->refreshptr = nil; - if(drawgoodname(dimage)) + if(drawgoodname(client, dimage)) memldelete(l); else memlfree(l); - drawfreedscreen(ds); + drawfreedscreen(client, ds); }else{ if(l->screenref==0) freememimage(l); @@ -575,8 +460,7 @@ drawfreedimage(DImage *dimage) free(dimage); } -static -void +static void drawuninstallscreen(Client *client, CScreen *this) { CScreen *cs, *next; @@ -584,14 +468,14 @@ drawuninstallscreen(Client *client, CScreen *this) cs = client->cscreen; if(cs == this){ client->cscreen = this->next; - drawfreedscreen(this->dscreen); + drawfreedscreen(client, this->dscreen); free(this); return; } while(next = cs->next){ /* assign = */ if(next == this){ cs->next = this->next; - drawfreedscreen(this->dscreen); + drawfreedscreen(client, this->dscreen); free(this); return; } @@ -599,8 +483,7 @@ drawuninstallscreen(Client *client, CScreen *this) } } -static -int +static int drawuninstall(Client *client, int id) { DImage *d, **l; @@ -608,28 +491,27 @@ drawuninstall(Client *client, int id) for(l=&client->dimage[id&HASHMASK]; (d=*l) != nil; l=&d->next){ if(d->id == id){ *l = d->next; - drawfreedimage(d); + drawfreedimage(client, d); return 0; } } return -1; } -static -int +static int drawaddname(Client *client, DImage *di, int n, char *str, char **err) { DName *name, *ename, *new, *t; char *ns; - name = sdraw.name; - ename = &name[sdraw.nname]; + name = client->name; + ename = &name[client->nname]; for(; name<ename; name++) if(drawcmp(name->name, str, n) == 0){ *err = "image name in use"; return -1; } - t = mallocz((sdraw.nname+1)*sizeof(DName), 1); + t = mallocz((client->nname+1)*sizeof(DName), 1); ns = malloc(n+1); if(t == nil || ns == nil){ free(t); @@ -637,16 +519,16 @@ drawaddname(Client *client, DImage *di, int n, char *str, char **err) *err = "out of memory"; return -1; } - memmove(t, sdraw.name, sdraw.nname*sizeof(DName)); - free(sdraw.name); - sdraw.name = t; - new = &sdraw.name[sdraw.nname++]; + memmove(t, client->name, client->nname*sizeof(DName)); + free(client->name); + client->name = t; + new = &client->name[client->nname++]; new->name = ns; memmove(new->name, str, n); new->name[n] = 0; new->dimage = di; new->client = client; - new->vers = ++sdraw.vers; + new->vers = ++client->namevers; return 0; } @@ -660,8 +542,7 @@ drawclientop(Client *cl) return op; } -static -Memimage* +static Memimage* drawimage(Client *client, uchar *a) { DImage *d; @@ -672,8 +553,7 @@ drawimage(Client *client, uchar *a) return d->image; } -static -void +static void drawrectangle(Rectangle *r, uchar *a) { r->min.x = BGLONG(a+0*4); @@ -682,16 +562,14 @@ drawrectangle(Rectangle *r, uchar *a) r->max.y = BGLONG(a+3*4); } -static -void +static void drawpoint(Point *p, uchar *a) { p->x = BGLONG(a+0*4); p->y = BGLONG(a+1*4); } -static -Point +static Point drawchar(Memimage *dst, Point p, Memimage *src, Point *sp, DImage *font, int index, int op) { FChar *fc; @@ -711,8 +589,7 @@ drawchar(Memimage *dst, Point p, Memimage *src, Point *sp, DImage *font, int ind return p; } -static -uchar* +static uchar* drawcoord(uchar *p, uchar *maxp, int oldx, int *newx) { int b, x; @@ -738,12 +615,9 @@ drawcoord(uchar *p, uchar *maxp, int oldx, int *newx) } int -_drawmsgread(void *a, int n) +draw_dataread(Client *cl, void *a, int n) { - Client *cl; - - qlock(&sdraw.lk); - cl = client0; + qlock(&cl->drawlk); if(cl->readdata == nil){ werrstr("no draw data"); goto err; @@ -756,23 +630,22 @@ _drawmsgread(void *a, int n) memmove(a, cl->readdata, cl->nreaddata); free(cl->readdata); cl->readdata = nil; - qunlock(&sdraw.lk); + qunlock(&cl->drawlk); return n; err: - qunlock(&sdraw.lk); + qunlock(&cl->drawlk); return -1; } int -_drawmsgwrite(void *v, int n) +draw_datawrite(Client *client, void *v, int n) { char cbuf[40], *err, ibuf[12*12+1], *s; int c, ci, doflush, dstid, e0, e1, esize, j, m; - int ni, nw, oesize, oldn, op, ox, oy, repl, scrnid, y; + int ni, nw, oesize, oldn, op, ox, oy, repl, scrnid, y; uchar *a, refresh, *u; u32int chan, value; - Client *client; CScreen *cs; DImage *di, *ddst, *dsrc, *font, *ll; DName *dn; @@ -786,11 +659,11 @@ _drawmsgwrite(void *v, int n) Refreshfn reffn; Refx *refx; - qlock(&sdraw.lk); + qlock(&client->drawlk); + rpc_gfxdrawlock(); a = v; m = 0; oldn = n; - client = client0; while((n-=m) > 0){ a += m; @@ -844,7 +717,7 @@ _drawmsgwrite(void *v, int n) l = memlalloc(scrn, r, reffn, 0, value); if(l == 0) goto Edrawmem; - addflush(l->layer->screenr); + addflush(client, l->layer->screenr); l->clipr = clipr; rectclip(&l->clipr, r); if(drawinstall(client, dstid, l, dscrn) == 0){ @@ -891,7 +764,7 @@ _drawmsgwrite(void *v, int n) dstid = BGLONG(a+1); if(dstid == 0) goto Ebadarg; - if(drawlookupdscreen(dstid)) + if(drawlookupdscreen(client, dstid)) goto Escreenexists; ddst = drawlookup(client, BGLONG(a+5), 1); dsrc = drawlookup(client, BGLONG(a+9), 1); @@ -935,7 +808,7 @@ _drawmsgwrite(void *v, int n) drawpoint(&q, a+37); op = drawclientop(client); memdraw(dst, r, src, p, mask, q, op); - dstflush(dstid, dst, r); + dstflush(client, dstid, dst, r); continue; /* toggle debugging: 'D' val[1] */ @@ -969,7 +842,7 @@ _drawmsgwrite(void *v, int n) err = "negative ellipse thickness"; goto error; } - + drawpoint(&sp, a+29); c = j; if(*a == 'E') @@ -984,7 +857,7 @@ _drawmsgwrite(void *v, int n) memarc(dst, p, e0, e1, c, src, sp, ox, oy, op); }else memellipse(dst, p, e0, e1, c, src, sp, op); - dstflush(dstid, dst, Rect(p.x-e0-j, p.y-e1-j, p.x+e0+j+1, p.y+e1+j+1)); + dstflush(client, dstid, dst, Rect(p.x-e0-j, p.y-e1-j, p.x+e0+j+1, p.y+e1+j+1)); continue; /* free: 'f' id[4] */ @@ -1049,7 +922,7 @@ _drawmsgwrite(void *v, int n) goto Eshortdraw; if(drawlookup(client, 0, 0)) goto Eimageexists; - drawinstall(client, 0, screenimage, 0); + drawinstall(client, 0, client->screenimage, 0); client->infoid = 0; continue; @@ -1061,7 +934,7 @@ _drawmsgwrite(void *v, int n) if(client->infoid < 0) goto Enodrawimage; if(client->infoid == 0){ - i = screenimage; + i = client->screenimage; if(i == nil) goto Enodrawimage; }else{ @@ -1073,11 +946,11 @@ _drawmsgwrite(void *v, int n) ni = sprint(ibuf, "%11d %11d %11s %11d %11d %11d %11d %11d" " %11d %11d %11d %11d ", client->clientid, - client->infoid, + client->infoid, chantostr(cbuf, i->chan), (i->flags&Frepl)==Frepl, i->r.min.x, i->r.min.y, i->r.max.x, i->r.max.y, - i->clipr.min.x, i->clipr.min.y, + i->clipr.min.x, i->clipr.min.y, i->clipr.max.x, i->clipr.max.y); free(client->readdata); client->readdata = malloc(ni); @@ -1087,7 +960,7 @@ _drawmsgwrite(void *v, int n) client->nreaddata = ni; client->infoid = -1; continue; - + /* query: 'Q' n[1] queryspec[n] */ case 'q': if(n < 2) @@ -1102,10 +975,10 @@ _drawmsgwrite(void *v, int n) err = "unknown query"; goto error; case 'd': /* dpi */ - if(forcedpi) - fmtprint(&fmt, "%11d ", forcedpi); + if(client->forcedpi) + fmtprint(&fmt, "%11d ", client->forcedpi); else - fmtprint(&fmt, "%11d ", displaydpi); + fmtprint(&fmt, "%11d ", client->displaydpi); break; } } @@ -1169,7 +1042,7 @@ _drawmsgwrite(void *v, int n) if(dstid==0 || dst->layer!=nil){ /* BUG: this is terribly inefficient: update maximal containing rect*/ r = memlinebbox(p, q, e0, e1, j); - dstflush(dstid, dst, insetrect(r, -(1+1+j))); + dstflush(client, dstid, dst, insetrect(r, -(1+1+j))); } continue; @@ -1198,7 +1071,7 @@ _drawmsgwrite(void *v, int n) dstid = BGLONG(a+1); if(drawlookup(client, dstid, 0)) goto Eimageexists; - dn = drawlookupname(j, (char*)a+6); + dn = drawlookupname(client, j, (char*)a+6); if(dn == nil) goto Enoname; s = malloc(j+1); @@ -1239,12 +1112,12 @@ _drawmsgwrite(void *v, int n) if(drawaddname(client, di, j, (char*)a+7, &err) < 0) goto error; else{ - dn = drawlookupname(j, (char*)a+7); + dn = drawlookupname(client, j, (char*)a+7); if(dn == nil) goto Enoname; if(dn->dimage != di) goto Ewrongname; - drawdelname(dn); + drawdelname(client, dn); } continue; @@ -1266,8 +1139,8 @@ _drawmsgwrite(void *v, int n) goto error; } if(ni > 0){ - addflush(r); - addflush(dst->layer->screenr); + addflush(client, r); + addflush(client, dst->layer->screenr); ll = drawlookup(client, BGLONG(a+1), 1); drawrefreshscreen(ll, client); } @@ -1316,7 +1189,7 @@ _drawmsgwrite(void *v, int n) if(pp == nil) goto Enomem; doflush = 0; - if(dstid==0 || (dst->layer && dst->layer->screen->image->data == screenimage->data)) + if(dstid==0 || (dst->layer && dst->layer->screen->image->data == client->screenimage->data)) doflush = 1; /* simplify test in loop */ ox = oy = 0; esize = 0; @@ -1353,12 +1226,12 @@ _drawmsgwrite(void *v, int n) combinerect(&r, Rect(p.x-esize, p.y-esize, p.x+esize+1, p.y+esize+1)); } if(rectclip(&r, dst->clipr)) /* should perhaps be an arg to dstflush */ - dstflush(dstid, dst, r); + dstflush(client, dstid, dst, r); } pp[y] = p; } if(y == 1) - dstflush(dstid, dst, Rect(p.x-esize, p.y-esize, p.x+esize+1, p.y+esize+1)); + dstflush(client, dstid, dst, Rect(p.x-esize, p.y-esize, p.x+esize+1, p.y+esize+1)); op = drawclientop(client); if(*a == 'p') mempoly(dst, pp, ni, e0, e1, j, src, sp, op); @@ -1462,7 +1335,7 @@ _drawmsgwrite(void *v, int n) } dst->clipr = clipr; p.y -= font->ascent; - dstflush(dstid, dst, Rect(p.x, p.y, q.x, p.y+Dy(font->image->r))); + dstflush(client, dstid, dst, Rect(p.x, p.y, q.x, p.y+Dy(font->image->r))); continue; /* use public screen: 'S' id[4] chan[4] */ @@ -1473,7 +1346,7 @@ _drawmsgwrite(void *v, int n) dstid = BGLONG(a+1); if(dstid == 0) goto Ebadarg; - dscrn = drawlookupdscreen(dstid); + dscrn = drawlookupdscreen(client, dstid); if(dscrn==0 || (dscrn->public==0 && dscrn->owner!=client)) goto Enodrawscreen; if(dscrn->screen->image->chan != BGLONG(a+5)){ @@ -1522,9 +1395,9 @@ _drawmsgwrite(void *v, int n) memltofrontn(lp, nw); else memltorearn(lp, nw); - if(lp[0]->layer->screen->image->data == screenimage->data) + if(lp[0]->layer->screen->image->data == client->screenimage->data) for(j=0; j<nw; j++) - addflush(lp[j]->layer->screenr); + addflush(client, lp[j]->layer->screenr); free(lp); ll = drawlookup(client, BGLONG(a+1+1+2), 1); drawrefreshscreen(ll, client); @@ -1533,7 +1406,7 @@ _drawmsgwrite(void *v, int n) /* visible: 'v' */ case 'v': m = 1; - drawflush(); + drawflush(client); continue; /* write: 'y' id[4] R[4*4] data[x*1] */ @@ -1555,12 +1428,13 @@ _drawmsgwrite(void *v, int n) err = "bad writeimage call"; goto error; } - dstflush(dstid, dst, r); + dstflush(client, dstid, dst, r); m += y; continue; } } - qunlock(&sdraw.lk); + rpc_gfxdrawunlock(); + qunlock(&client->drawlk); return oldn - n; Enodrawimage: @@ -1630,8 +1504,7 @@ Ebadarg: error: werrstr("%s", err); - qunlock(&sdraw.lk); + rpc_gfxdrawunlock(); + qunlock(&client->drawlk); return -1; } - - diff --git a/src/cmd/devdraw/devdraw.h b/src/cmd/devdraw/devdraw.h index f768735f..4980ed90 100644 --- a/src/cmd/devdraw/devdraw.h +++ b/src/cmd/devdraw/devdraw.h @@ -1,10 +1,229 @@ -int _drawmsgread(void*, int); -int _drawmsgwrite(void*, int); -void _initdisplaymemimage(Memimage*); -int _latin1(Rune*, int); -int parsewinsize(char*, Rectangle*, int*); + +#define NHASH (1<<5) +#define HASHMASK (NHASH-1) + +typedef struct Kbdbuf Kbdbuf; +typedef struct Mousebuf Mousebuf; +typedef struct Tagbuf Tagbuf; + +typedef struct Client Client; +typedef struct DImage DImage; +typedef struct DScreen DScreen; +typedef struct CScreen CScreen; +typedef struct FChar FChar; +typedef struct Refresh Refresh; +typedef struct Refx Refx; +typedef struct DName DName; + +struct Kbdbuf +{ + Rune r[256]; + int ri; + int wi; + int stall; + int alting; + Rune k[10]; + int nk; +}; + +struct Mousebuf +{ + Mouse m[256]; + Mouse last; + int ri; + int wi; + int stall; + int resized; +}; + +struct Tagbuf +{ + int t[256]; + int ri; + int wi; +}; + +struct Client +{ + int rfd; + + // wfdlk protects writes to wfd, which can be issued from either + // the RPC thread or the graphics thread. + QLock wfdlk; + int wfd; + uchar* mbuf; + int nmbuf; + + char* wsysid; + + // drawlk protects the draw data structures. + // It can be acquired by an RPC thread or a graphics thread + // but must not be held on one thread while waiting for the other. + QLock drawlk; + /*Ref r;*/ + DImage* dimage[NHASH]; + CScreen* cscreen; + Refresh* refresh; + Rendez refrend; + uchar* readdata; + int nreaddata; + int busy; + int clientid; + int slot; + int refreshme; + int infoid; + int op; + int displaydpi; + int forcedpi; + int waste; + Rectangle flushrect; + Memimage *screenimage; + DScreen* dscreen; + int nname; + DName* name; + int namevers; + + // Only accessed/modified by the graphics thread. + const void* view; + + // eventlk protects the keyboard and mouse events. + QLock eventlk; + Kbdbuf kbd; + Mousebuf mouse; + Tagbuf kbdtags; + Tagbuf mousetags; + Rectangle mouserect; +}; + +struct Refresh +{ + DImage* dimage; + Rectangle r; + Refresh* next; +}; + +struct Refx +{ + Client* client; + DImage* dimage; +}; + +struct DName +{ + char *name; + Client *client; + DImage* dimage; + int vers; +}; + +struct FChar +{ + int minx; /* left edge of bits */ + int maxx; /* right edge of bits */ + uchar miny; /* first non-zero scan-line */ + uchar maxy; /* last non-zero scan-line + 1 */ + schar left; /* offset of baseline */ + uchar width; /* width of baseline */ +}; + +/* + * Reference counts in DImages: + * one per open by original client + * one per screen image or fill + * one per image derived from this one by name + */ +struct DImage +{ + int id; + int ref; + char *name; + int vers; + Memimage* image; + int ascent; + int nfchar; + FChar* fchar; + DScreen* dscreen; /* 0 if not a window */ + DImage* fromname; /* image this one is derived from, by name */ + DImage* next; +}; + +struct CScreen +{ + DScreen* dscreen; + CScreen* next; +}; + +struct DScreen +{ + int id; + int public; + int ref; + DImage *dimage; + DImage *dfill; + Memscreen* screen; + Client* owner; + DScreen* next; +}; + +// For the most part, the graphics driver-specific code in files +// like mac-screen.m runs in the graphics library's main thread, +// while the RPC service code in srv.c runs on the RPC service thread. +// The exceptions in each file, which are called by the other, +// are marked with special prefixes: gfx_* indicates code that +// is in srv.c but nonetheless runs on the main graphics thread, +// while rpc_* indicates code that is in, say, mac-screen.m but +// nonetheless runs on the RPC service thread. +// +// The gfx_* and rpc_* calls typically synchronize with the other +// code in the file by acquiring a lock (or running a callback on the +// target thread, which amounts to the same thing). +// To avoid deadlock, callers of those routines must not hold any locks. + +// gfx_* routines are called on the graphics thread, +// invoked from graphics driver callbacks to do RPC work. +// No locks are held on entry. +void gfx_abortcompose(Client*); +void gfx_keystroke(Client*, int); +void gfx_main(void); +void gfx_mousetrack(Client*, int, int, int, uint); +void gfx_replacescreenimage(Client*, Memimage*); +void gfx_started(void); + +// rpc_* routines are called on the RPC thread, +// invoked by the RPC server code to do graphics work. +// No locks are held on entry. +Memimage *rpc_attach(Client*, char*, char*); +char* rpc_getsnarf(void); +void rpc_putsnarf(char*); +void rpc_resizeimg(Client*); +void rpc_resizewindow(Client*, Rectangle); +void rpc_serve(Client*); +void rpc_setcursor(Client*, Cursor*, Cursor2*); +void rpc_setlabel(Client*, char*); +void rpc_setmouse(Client*, Point); +void rpc_shutdown(void); +void rpc_topwin(Client*); +void rpc_main(void); +void rpc_bouncemouse(Client*, Mouse); +void rpc_flush(Client*, Rectangle); + +// rpc_gfxdrawlock and rpc_gfxdrawunlock +// are called around drawing operations to lock and unlock +// access to the graphics display, for systems where the +// individual memdraw operations use the graphics display (X11, not macOS). +void rpc_gfxdrawlock(void); +void rpc_gfxdrawunlock(void); + +// draw* routines are called on the RPC thread, +// invoked by the RPC server to do pixel pushing. +// No locks are held on entry. +int draw_dataread(Client*, void*, int); +int draw_datawrite(Client*, void*, int); +void draw_initdisplaymemimage(Client*, Memimage*); + +// utility routines +int latin1(Rune*, int); int mouseswap(int); -void abortcompose(void); +int parsewinsize(char*, Rectangle*, int*); -extern int displaydpi; -extern int forcedpi; +extern Client *client0; // set in single-client mode diff --git a/src/cmd/devdraw/drawclient.c b/src/cmd/devdraw/drawclient.c index 87df3f18..71c7142d 100644 --- a/src/cmd/devdraw/drawclient.c +++ b/src/cmd/devdraw/drawclient.c @@ -4,7 +4,7 @@ #include <draw.h> #include <mouse.h> #include <cursor.h> -#include <drawsrv.h> +#include <drawfcall.h> typedef struct Cmd Cmd; struct Cmd { @@ -20,7 +20,7 @@ void startsrv(void) { int pid, p[2]; - + if(pipe(p) < 0) sysfatal("pipe"); if((pid=fork()) < 0) @@ -29,7 +29,7 @@ startsrv(void) close(p[0]); dup(p[1], 0); dup(p[1], 1); - execl("o.drawsrv", "o.drawsrv", "-D", nil); + execl("./o.devdraw", "o.devdraw", "-D", nil); sysfatal("exec: %r"); } close(p[1]); @@ -47,7 +47,7 @@ fprint(2, "write %d to %d\n", n, fd); n = readwsysmsg(fd, buf, sizeof buf); nn = convM2W(buf, n, m); assert(nn == n); - if(m->op == Rerror) + if(m->type == Rerror) return -1; return 0; } @@ -56,12 +56,11 @@ void cmdinit(int argc, char **argv) { Wsysmsg m; - + memset(&m, 0, sizeof m); - m.op = Tinit; + m.type = Tinit; m.winsize = "100x100"; m.label = "label"; - m.font = ""; if(domsg(&m) < 0) sysfatal("domsg"); } @@ -70,9 +69,9 @@ void cmdmouse(int argc, char **argv) { Wsysmsg m; - + memset(&m, 0, sizeof m); - m.op = Trdmouse; + m.type = Trdmouse; if(domsg(&m) < 0) sysfatal("domsg"); print("%c %d %d %d\n", @@ -86,12 +85,12 @@ void cmdkbd(int argc, char **argv) { Wsysmsg m; - + memset(&m, 0, sizeof m); - m.op = Trdkbd; + m.type = Trdkbd; if(domsg(&m) < 0) sysfatal("domsg"); - print("%s\n", m.runes); + print("%d\n", m.rune); } Cmd cmdtab[] = { @@ -125,4 +124,3 @@ fprint(2, "%s...\n", p); } exits(0); } - diff --git a/src/cmd/devdraw/latin1.c b/src/cmd/devdraw/latin1.c index 09c67523..a3d13a08 100644 --- a/src/cmd/devdraw/latin1.c +++ b/src/cmd/devdraw/latin1.c @@ -46,7 +46,7 @@ unicode(Rune *k) * is minus the required n. */ int -_latin1(Rune *k, int n) +latin1(Rune *k, int n) { struct cvlist *l; int c; @@ -58,7 +58,7 @@ _latin1(Rune *k, int n) else return -5; } - + for(l=latintab; l->ld!=0; l++) if(k[0] == l->ld[0]){ if(n == 1) diff --git a/src/cmd/devdraw/osx-draw.c b/src/cmd/devdraw/mac-draw.c index d3f08331..fdf7acec 100644 --- a/src/cmd/devdraw/osx-draw.c +++ b/src/cmd/devdraw/mac-draw.c @@ -56,4 +56,3 @@ unloadmemimage(Memimage *i, Rectangle r, uchar *data, int ndata) { return _unloadmemimage(i, r, data, ndata); } - diff --git a/src/cmd/devdraw/cocoa-screen-metal.m b/src/cmd/devdraw/mac-screen.m index 984ede03..e02a2524 100644 --- a/src/cmd/devdraw/cocoa-screen-metal.m +++ b/src/cmd/devdraw/mac-screen.m @@ -12,13 +12,14 @@ #include <u.h> #include <libc.h> -#include "cocoa-thread.h" +#include <thread.h> #include <draw.h> #include <memdraw.h> -#include <keyboard.h> +#include <memlayer.h> +#include <mouse.h> #include <cursor.h> -#include "cocoa-screen.h" -#include "osx-keycodes.h" +#include <keyboard.h> +#include <drawfcall.h> #include "devdraw.h" #include "bigarrow.h" #include "glendapng.h" @@ -26,78 +27,29 @@ AUTOFRAMEWORK(Cocoa) AUTOFRAMEWORK(Metal) AUTOFRAMEWORK(QuartzCore) +AUTOFRAMEWORK(CoreFoundation) #define LOG if(0)NSLog +// TODO: Maintain list of views for dock menu. + static void setprocname(const char*); static uint keycvt(uint); static uint msec(void); -static Memimage* initimg(void); -void -usage(void) -{ - fprint(2, "usage: devdraw (don't run directly)\n"); - threadexitsall("usage"); -} +@class DrawView; +@class DrawLayer; -@interface AppDelegate : NSObject<NSApplicationDelegate,NSWindowDelegate> -+ (void)callservep9p:(id)arg; -+ (void)makewin:(NSValue *)v; -+ (void)callkicklabel:(NSString *)v; -+ (void)callsetNeedsDisplayInRect:(NSValue *)v; -+ (void)callsetcursor:(NSValue *)v; -@end -@interface DevDrawView : NSView<NSTextInputClient> -- (void)clearInput; -- (void)getmouse:(NSEvent *)e; -- (void)sendmouse:(NSUInteger)b; -- (void)resetLastInputRect; -- (void)enlargeLastInputRect:(NSRect)r; -@end -@interface DrawLayer : CAMetalLayer +@interface AppDelegate : NSObject<NSApplicationDelegate> @end static AppDelegate *myApp = NULL; -static DevDrawView *myContent = NULL; -static NSWindow *win = NULL; -static NSCursor *currentCursor = NULL; - -static DrawLayer *layer; -static id<MTLDevice> device; -static id<MTLCommandQueue> commandQueue; -static id<MTLTexture> texture; - -static Memimage *img = NULL; - -static QLock snarfl; void -threadmain(int argc, char **argv) +gfx_main(void) { - /* - * Move the protocol off stdin/stdout so that - * any inadvertent prints don't screw things up. - */ - dup(0,3); - dup(1,4); - close(0); - close(1); - open("/dev/null", OREAD); - open("/dev/null", OWRITE); - - ARGBEGIN{ - case 'D': /* for good ps -a listings */ - break; - case 'f': /* fall through for backward compatibility */ - case 'g': - case 'b': - break; - default: - usage(); - }ARGEND - - setprocname(argv0); + if(client0) + setprocname(argv0); @autoreleasepool{ [NSApplication sharedApplication]; @@ -108,16 +60,157 @@ threadmain(int argc, char **argv) } } + +void +rpc_shutdown(void) +{ + [NSApp terminate:myApp]; +} + @implementation AppDelegate +- (void)applicationDidFinishLaunching:(id)arg +{ + NSMenu *m, *sm; + NSData *d; + NSImage *i; + + LOG(@"applicationDidFinishLaunching"); + + sm = [NSMenu new]; + [sm addItemWithTitle:@"Toggle Full Screen" action:@selector(toggleFullScreen:) keyEquivalent:@"f"]; + [sm addItemWithTitle:@"Hide" action:@selector(hide:) keyEquivalent:@"h"]; + [sm addItemWithTitle:@"Quit" action:@selector(terminate:) keyEquivalent:@"q"]; + m = [NSMenu new]; + [m addItemWithTitle:@"DEVDRAW" action:NULL keyEquivalent:@""]; + [m setSubmenu:sm forItem:[m itemWithTitle:@"DEVDRAW"]]; + [NSApp setMainMenu:m]; + + d = [[NSData alloc] initWithBytes:glenda_png length:(sizeof glenda_png)]; + i = [[NSImage alloc] initWithData:d]; + [NSApp setApplicationIconImage:i]; + [[NSApp dockTile] display]; + + gfx_started(); +} + +- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)theApplication { + return client0 != nil; +} +@end + +@interface DrawLayer : CAMetalLayer +@property (nonatomic, retain) id<MTLCommandQueue> cmd; +@property (nonatomic, retain) id<MTLTexture> texture; +@end + +@implementation DrawLayer +- (void)display +{ + LOG(@"display"); + LOG(@"display query drawable"); + + @autoreleasepool{ + id<CAMetalDrawable> drawable = [self nextDrawable]; + if(!drawable){ + LOG(@"display couldn't get drawable"); + [self setNeedsDisplay]; + return; + } + + LOG(@"display got drawable"); + + id<MTLCommandBuffer> cbuf = [self.cmd commandBuffer]; + id<MTLBlitCommandEncoder> blit = [cbuf blitCommandEncoder]; + [blit copyFromTexture:self.texture + sourceSlice:0 + sourceLevel:0 + sourceOrigin:MTLOriginMake(0, 0, 0) + sourceSize:MTLSizeMake(self.texture.width, self.texture.height, self.texture.depth) + toTexture:drawable.texture + destinationSlice:0 + destinationLevel:0 + destinationOrigin:MTLOriginMake(0, 0, 0)]; + [blit endEncoding]; + + [cbuf presentDrawable:drawable]; + drawable = nil; + [cbuf addCompletedHandler:^(id<MTLCommandBuffer> cmdBuff){ + if(cmdBuff.error){ + NSLog(@"command buffer finished with error: %@", + cmdBuff.error.localizedDescription); + }else + LOG(@"command buffer finishes present drawable"); + }]; + [cbuf commit]; + } + LOG(@"display commit"); +} +@end + +@interface DrawView : NSView<NSTextInputClient,NSWindowDelegate> +@property (nonatomic, assign) Client *client; +@property (nonatomic, retain) DrawLayer *dlayer; +@property (nonatomic, retain) NSWindow *win; +@property (nonatomic, retain) NSCursor *currentCursor; +@property (nonatomic, assign) Memimage *img; + +- (id)attach:(Client*)client winsize:(char*)winsize label:(char*)label; +- (void)topwin; +- (void)setlabel:(char*)label; +- (void)setcursor:(Cursor*)c cursor2:(Cursor2*)c2; +- (void)setmouse:(Point)p; +- (void)clearInput; +- (void)getmouse:(NSEvent*)e; +- (void)sendmouse:(NSUInteger)b; +- (void)resetLastInputRect; +- (void)enlargeLastInputRect:(NSRect)r; +@end -+ (void)callservep9p:(id)arg +@implementation DrawView { - servep9p(); - [NSApp terminate:self]; + NSMutableString *_tmpText; + NSRange _markedRange; + NSRange _selectedRange; + NSRect _lastInputRect; // The view is flipped, this is not. + BOOL _tapping; + NSUInteger _tapFingers; + NSUInteger _tapTime; } -+ (void)makewin:(NSValue *)v +- (id)init { + LOG(@"View init"); + self = [super init]; + [self setAllowedTouchTypes:NSTouchTypeMaskDirect|NSTouchTypeMaskIndirect]; + _tmpText = [[NSMutableString alloc] initWithCapacity:2]; + _markedRange = NSMakeRange(NSNotFound, 0); + _selectedRange = NSMakeRange(0, 0); + return self; +} + +- (CALayer*)makeBackingLayer { return [DrawLayer layer]; } +- (BOOL)wantsUpdateLayer { return YES; } +- (BOOL)isOpaque { return YES; } +- (BOOL)isFlipped { return YES; } +- (BOOL)acceptsFirstResponder { return YES; } + +// rpc_attach allocates a new screen window with the given label and size +// and attaches it to client c (by setting c->view). +Memimage* +rpc_attach(Client *c, char *label, char *winsize) +{ + LOG(@"attachscreen(%s, %s)", label, winsize); + + dispatch_sync(dispatch_get_main_queue(), ^(void) { + @autoreleasepool { + DrawView *view = [[DrawView new] attach:c winsize:winsize label:label]; + [view initimg]; + } + }); + return ((__bridge DrawView*)c->view).img; +} + +- (id)attach:(Client*)client winsize:(char*)winsize label:(char*)label { NSRect r, sr; Rectangle wr; int set; @@ -129,15 +222,12 @@ threadmain(int argc, char **argv) | NSWindowStyleMaskMiniaturizable | NSWindowStyleMaskResizable; + s = winsize; sr = [[NSScreen mainScreen] frame]; r = [[NSScreen mainScreen] visibleFrame]; - s = [v pointerValue]; LOG(@"makewin(%s)", s); - if(s && *s){ - if(parsewinsize(s, &wr, &set) < 0) - sysfatal("%r"); - }else{ + if(s == nil || *s == '\0' || parsewinsize(s, &wr, &set) < 0) { wr = Rect(0, 0, sr.size.width*2/3, sr.size.height*2/3); set = 0; } @@ -148,7 +238,7 @@ threadmain(int argc, char **argv) r.size.height = fmin(Dy(wr), r.size.height); r = [NSWindow contentRectForFrameRect:r styleMask:Winstyle]; - win = [[NSWindow alloc] + NSWindow *win = [[NSWindow alloc] initWithContentRect:r styleMask:Winstyle backing:NSBackingStoreBuffered defer:NO]; @@ -161,14 +251,17 @@ threadmain(int argc, char **argv) [win setOpaque:YES]; [win setRestorable:NO]; [win setAcceptsMouseMovedEvents:YES]; - [win setDelegate:myApp]; - - myContent = [DevDrawView new]; - [win setContentView:myContent]; - [myContent setWantsLayer:YES]; - [myContent setLayerContentsRedrawPolicy:NSViewLayerContentsRedrawOnSetNeedsDisplay]; - - device = nil; + + client->view = CFBridgingRetain(self); + self.client = client; + self.win = win; + self.currentCursor = nil; + [win setContentView:self]; + [win setDelegate:self]; + [self setWantsLayer:YES]; + [self setLayerContentsRedrawPolicy:NSViewLayerContentsRedrawOnSetNeedsDisplay]; + + id<MTLDevice> device = nil; allDevices = MTLCopyAllDevices(); for(id mtlDevice in allDevices) { if ([mtlDevice isLowPower] && ![mtlDevice isRemovable]) { @@ -179,17 +272,19 @@ threadmain(int argc, char **argv) if(!device) device = MTLCreateSystemDefaultDevice(); - commandQueue = [device newCommandQueue]; - - layer = (DrawLayer *)[myContent layer]; + DrawLayer *layer = (DrawLayer*)[self layer]; + self.dlayer = layer; layer.device = device; + layer.cmd = [device newCommandQueue]; layer.pixelFormat = MTLPixelFormatBGRA8Unorm; layer.framebufferOnly = YES; layer.opaque = YES; // We use a default transparent layer on top of the CAMetalLayer. // This seems to make fullscreen applications behave. - { + // Specifically, without this code if you enter full screen with Cmd-F, + // the screen goes black until the first mouse click. + if(1) { CALayer *stub = [CALayer layer]; stub.frame = CGRectMake(0, 0, 1, 1); [stub setNeedsDisplay]; @@ -198,61 +293,78 @@ threadmain(int argc, char **argv) [NSEvent setMouseCoalescingEnabled:NO]; - topwin(); + [self topwin]; + [self setlabel:label]; + [self setcursor:nil cursor2:nil]; + + return self; } -+ (void)callkicklabel:(NSString *)s +// rpc_topwin moves the window to the top of the desktop. +// Called from an RPC thread with no client lock held. +void +rpc_topwin(Client *c) { - LOG(@"callkicklabel(%@)", s); - [win setTitle:s]; - [[NSApp dockTile] setBadgeLabel:s]; + DrawView *view = (__bridge DrawView*)c->view; + dispatch_sync(dispatch_get_main_queue(), ^(void) { + [view topwin]; + }); } +- (void)topwin { + [self.win makeKeyAndOrderFront:nil]; + [NSApp activateIgnoringOtherApps:YES]; +} -+ (void)callsetNeedsDisplayInRect:(NSValue *)v +// rpc_setlabel updates the client window's label. +// If label == nil, the call is a no-op. +// Called from an RPC thread with no client lock held. +void +rpc_setlabel(Client *client, char *label) { - NSRect r; - dispatch_time_t time; - - r = [v rectValue]; - LOG(@"callsetNeedsDisplayInRect(%g, %g, %g, %g)", r.origin.x, r.origin.y, r.size.width, r.size.height); - r = [win convertRectFromBacking:r]; - LOG(@"setNeedsDisplayInRect(%g, %g, %g, %g)", r.origin.x, r.origin.y, r.size.width, r.size.height); - [layer setNeedsDisplayInRect:r]; - - time = dispatch_time(DISPATCH_TIME_NOW, 16 * NSEC_PER_MSEC); - dispatch_after(time, dispatch_get_main_queue(), ^(void){ - [layer setNeedsDisplayInRect:r]; + DrawView *view = (__bridge DrawView*)client->view; + dispatch_sync(dispatch_get_main_queue(), ^(void){ + [view setlabel:label]; }); - - [myContent enlargeLastInputRect:r]; } -typedef struct Cursors Cursors; -struct Cursors { - Cursor *c; - Cursor2 *c2; -}; +- (void)setlabel:(char*)label { + LOG(@"setlabel(%s)", label); + if(label == nil) + return; + + @autoreleasepool{ + NSString *s = [[NSString alloc] initWithUTF8String:label]; + [self.win setTitle:s]; + if(client0) + [[NSApp dockTile] setBadgeLabel:s]; + } +} -+ (void)callsetcursor:(NSValue *)v +// rpc_setcursor updates the client window's cursor image. +// Either c and c2 are both non-nil, or they are both nil to use the default arrow. +// Called from an RPC thread with no client lock held. +void +rpc_setcursor(Client *client, Cursor *c, Cursor2 *c2) { - Cursors *cs; - Cursor *c; - Cursor2 *c2; + DrawView *view = (__bridge DrawView*)client->view; + dispatch_sync(dispatch_get_main_queue(), ^(void){ + [view setcursor:c cursor2:c2]; + }); +} + +- (void)setcursor:(Cursor*)c cursor2:(Cursor2*)c2 { + if(!c) { + c = &bigarrow; + c2 = &bigarrow2; + } + NSBitmapImageRep *r, *r2; NSImage *i; NSPoint p; uchar *plane[5], *plane2[5]; uint b; - cs = [v pointerValue]; - c = cs->c; - if(!c) - c = &bigarrow; - c2 = cs->c2; - if(!c2) - c2 = &bigarrow2; - r = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:nil pixelsWide:16 @@ -287,128 +399,166 @@ struct Cursors { plane2[1][b] = c2->set[b] | c2->clr[b]; } - // For checking out the cursor bitmap image -/* - static BOOL saveimg = YES; - if(saveimg){ + static BOOL debug = NO; + if(debug){ NSData *data = [r representationUsingType: NSBitmapImageFileTypeBMP properties: @{}]; [data writeToFile: @"/tmp/r.bmp" atomically: NO]; data = [r2 representationUsingType: NSBitmapImageFileTypeBMP properties: @{}]; [data writeToFile: @"/tmp/r2.bmp" atomically: NO]; - saveimg = NO; + debug = NO; } -*/ i = [[NSImage alloc] initWithSize:NSMakeSize(16, 16)]; [i addRepresentation:r2]; [i addRepresentation:r]; p = NSMakePoint(-c->offset.x, -c->offset.y); - currentCursor = [[NSCursor alloc] initWithImage:i hotSpot:p]; + self.currentCursor = [[NSCursor alloc] initWithImage:i hotSpot:p]; + [self.win invalidateCursorRectsForView:self]; +} - [win invalidateCursorRectsForView:myContent]; +- (void)initimg { + @autoreleasepool { + CGFloat scale; + NSSize size; + MTLTextureDescriptor *textureDesc; + + size = [self convertSizeToBacking:[self bounds].size]; + self.client->mouserect = Rect(0, 0, size.width, size.height); + + LOG(@"initimg %.0f %.0f", size.width, size.height); + + self.img = allocmemimage(self.client->mouserect, XRGB32); + if(self.img == nil) + panic("allocmemimage: %r"); + if(self.img->data == nil) + panic("img->data == nil"); + + textureDesc = [MTLTextureDescriptor + texture2DDescriptorWithPixelFormat:MTLPixelFormatBGRA8Unorm + width:size.width + height:size.height + mipmapped:NO]; + textureDesc.allowGPUOptimizedContents = YES; + textureDesc.usage = MTLTextureUsageShaderRead; + textureDesc.cpuCacheMode = MTLCPUCacheModeWriteCombined; + self.dlayer.texture = [self.dlayer.device newTextureWithDescriptor:textureDesc]; + + scale = [self.win backingScaleFactor]; + [self.dlayer setDrawableSize:size]; + [self.dlayer setContentsScale:scale]; + + // NOTE: This is not really the display DPI. + // On retina, scale is 2; otherwise it is 1. + // This formula gives us 220 for retina, 110 otherwise. + // That's not quite right but it's close to correct. + // https://en.wikipedia.org/wiki/Retina_display#Models + self.client->displaydpi = scale * 110; + } } -- (void)applicationDidFinishLaunching:(id)arg +// rpc_flush flushes changes to view.img's rectangle r +// to the on-screen window, making them visible. +// Called from an RPC thread with no client lock held. +void +rpc_flush(Client *client, Rectangle r) { - NSMenu *m, *sm; - NSData *d; - NSImage *i; - - LOG(@"applicationDidFinishLaunching"); - - sm = [NSMenu new]; - [sm addItemWithTitle:@"Toggle Full Screen" action:@selector(toggleFullScreen:) keyEquivalent:@"f"]; - [sm addItemWithTitle:@"Hide" action:@selector(hide:) keyEquivalent:@"h"]; - [sm addItemWithTitle:@"Quit" action:@selector(terminate:) keyEquivalent:@"q"]; - m = [NSMenu new]; - [m addItemWithTitle:@"DEVDRAW" action:NULL keyEquivalent:@""]; - [m setSubmenu:sm forItem:[m itemWithTitle:@"DEVDRAW"]]; - [NSApp setMainMenu:m]; + DrawView *view = (__bridge DrawView*)client->view; + dispatch_async(dispatch_get_main_queue(), ^(void){ + [view flush:r]; + }); +} - d = [[NSData alloc] initWithBytes:glenda_png length:(sizeof glenda_png)]; - i = [[NSImage alloc] initWithData:d]; - [NSApp setApplicationIconImage:i]; - [[NSApp dockTile] display]; +- (void)flush:(Rectangle)r { + @autoreleasepool{ + if(!rectclip(&r, Rect(0, 0, self.dlayer.texture.width, self.dlayer.texture.height)) || !rectclip(&r, self.img->r)) + return; + + // self.client->drawlk protects the pixel data in self.img. + // In addition to avoiding a technical data race, + // the lock avoids drawing partial updates, which makes + // animations like sweeping windows much less flickery. + qlock(&self.client->drawlk); + [self.dlayer.texture + replaceRegion:MTLRegionMake2D(r.min.x, r.min.y, Dx(r), Dy(r)) + mipmapLevel:0 + withBytes:byteaddr(self.img, Pt(r.min.x, r.min.y)) + bytesPerRow:self.img->width*sizeof(u32int)]; + qunlock(&self.client->drawlk); - [NSThread - detachNewThreadSelector:@selector(callservep9p:) - toTarget:[self class] withObject:nil]; -} + NSRect nr = NSMakeRect(r.min.x, r.min.y, Dx(r), Dy(r)); + dispatch_time_t time; -- (NSApplicationPresentationOptions)window:(id)arg - willUseFullScreenPresentationOptions:(NSApplicationPresentationOptions)proposedOptions { - NSApplicationPresentationOptions o; - o = proposedOptions; - o &= ~(NSApplicationPresentationAutoHideDock | NSApplicationPresentationAutoHideMenuBar); - o |= NSApplicationPresentationHideDock | NSApplicationPresentationHideMenuBar; - return o; -} + LOG(@"callsetNeedsDisplayInRect(%g, %g, %g, %g)", nr.origin.x, nr.origin.y, nr.size.width, nr.size.height); + nr = [self.win convertRectFromBacking:nr]; + LOG(@"setNeedsDisplayInRect(%g, %g, %g, %g)", nr.origin.x, nr.origin.y, nr.size.width, nr.size.height); + [self.dlayer setNeedsDisplayInRect:nr]; -- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)theApplication { - return YES; -} + time = dispatch_time(DISPATCH_TIME_NOW, 16 * NSEC_PER_MSEC); + dispatch_after(time, dispatch_get_main_queue(), ^(void){ + [self.dlayer setNeedsDisplayInRect:nr]; + }); -- (void)windowDidResize:(NSNotification *)notification -{ - if(![myContent inLiveResize] && img) { - resizeimg(); + [self enlargeLastInputRect:nr]; } } -- (void)windowDidBecomeKey:(id)arg +// rpc_resizeimg forces the client window to discard its current window and make a new one. +// It is called when the user types Cmd-R to toggle whether retina mode is forced. +// Called from an RPC thread with no client lock held. +void +rpc_resizeimg(Client *c) { - [myContent sendmouse:0]; + DrawView *view = (__bridge DrawView*)c->view; + dispatch_sync(dispatch_get_main_queue(), ^(void){ + [view resizeimg]; + }); } -@end - -@implementation DevDrawView -{ - NSMutableString *_tmpText; - NSRange _markedRange; - NSRange _selectedRange; - NSRect _lastInputRect; // The view is flipped, this is not. - BOOL _tapping; - NSUInteger _tapFingers; - NSUInteger _tapTime; +- (void)resizeimg { + [self initimg]; + gfx_replacescreenimage(self.client, self.img); + [self sendmouse:0]; } -- (id)init -{ - LOG(@"View init"); - self = [super init]; - [self setAllowedTouchTypes:NSTouchTypeMaskDirect|NSTouchTypeMaskIndirect]; - _tmpText = [[NSMutableString alloc] initWithCapacity:2]; - _markedRange = NSMakeRange(NSNotFound, 0); - _selectedRange = NSMakeRange(0, 0); - return self; +- (void)windowDidResize:(NSNotification *)notification { + if(![self inLiveResize] && self.img) { + [self resizeimg]; + } } - -- (CALayer *)makeBackingLayer +- (void)viewDidEndLiveResize { - LOG(@"makeBackingLayer"); - return [DrawLayer layer]; + [super viewDidEndLiveResize]; + if(self.img) + [self resizeimg]; } -- (BOOL)wantsUpdateLayer +- (void)viewDidChangeBackingProperties { - return YES; + [super viewDidChangeBackingProperties]; + if(self.img) + [self resizeimg]; } -- (BOOL)isOpaque +// rpc_resizewindow asks for the client window to be resized to size r. +// Called from an RPC thread with no client lock held. +void +rpc_resizewindow(Client *c, Rectangle r) { - return YES; -} + DrawView *view = (__bridge DrawView*)c->view; -- (BOOL)isFlipped -{ - return YES; + LOG(@"resizewindow %d %d %d %d", r.min.x, r.min.y, Dx(r), Dy(r)); + dispatch_async(dispatch_get_main_queue(), ^(void){ + NSSize s; + + s = [view convertSizeFromBacking:NSMakeSize(Dx(r), Dy(r))]; + [view.win setContentSize:s]; + }); } -- (BOOL)acceptsFirstResponder -{ - return YES; + +- (void)windowDidBecomeKey:(id)arg { + [self sendmouse:0]; } - (void)mouseMoved:(NSEvent*)e{ [self getmouse:e];} @@ -462,7 +612,7 @@ struct Cursors { b |= 4; [self sendmouse:b]; }else if(m & ~omod & NSEventModifierFlagOption) - keystroke(Kalt); + gfx_keystroke(self.client, Kalt); omod = m; } @@ -519,7 +669,7 @@ struct Cursors { if(b == 1){ m = [e modifierFlags]; if(m & NSEventModifierFlagOption){ - abortcompose(); + gfx_abortcompose(self.client); b = 2; }else if(m & NSEventModifierFlagCommand) @@ -534,48 +684,60 @@ struct Cursors { p = [self.window convertPointToBacking: [self.window mouseLocationOutsideOfEventStream]]; - p.y = Dy(mouserect) - p.y; + p.y = Dy(self.client->mouserect) - p.y; // LOG(@"(%g, %g) <- sendmouse(%d)", p.x, p.y, (uint)b); - mousetrack(p.x, p.y, b, msec()); + gfx_mousetrack(self.client, p.x, p.y, b, msec()); if(b && _lastInputRect.size.width && _lastInputRect.size.height) [self resetLastInputRect]; } -- (void)resetCursorRects { - [super resetCursorRects]; - [self addCursorRect:self.bounds cursor:currentCursor]; +// rpc_setmouse moves the mouse cursor. +// Called from an RPC thread with no client lock held. +void +rpc_setmouse(Client *c, Point p) +{ + DrawView *view = (__bridge DrawView*)c->view; + dispatch_async(dispatch_get_main_queue(), ^(void){ + [view setmouse:p]; + }); } -- (void)viewDidEndLiveResize -{ - [super viewDidEndLiveResize]; - if(img) - resizeimg(); +- (void)setmouse:(Point)p { + @autoreleasepool{ + NSPoint q; + + LOG(@"setmouse(%d,%d)", p.x, p.y); + q = [self.win convertPointFromBacking:NSMakePoint(p.x, p.y)]; + LOG(@"(%g, %g) <- fromBacking", q.x, q.y); + q = [self convertPoint:q toView:nil]; + LOG(@"(%g, %g) <- toWindow", q.x, q.y); + q = [self.win convertPointToScreen:q]; + LOG(@"(%g, %g) <- toScreen", q.x, q.y); + // Quartz has the origin of the "global display + // coordinate space" at the top left of the primary + // screen with y increasing downward, while Cocoa has + // the origin at the bottom left of the primary screen + // with y increasing upward. We flip the coordinate + // with a negative sign and shift upward by the height + // of the primary screen. + q.y = NSScreen.screens[0].frame.size.height - q.y; + LOG(@"(%g, %g) <- setmouse", q.x, q.y); + CGWarpMouseCursorPosition(NSPointToCGPoint(q)); + CGAssociateMouseAndMouseCursorPosition(true); + } } -- (void)viewDidChangeBackingProperties -{ - [super viewDidChangeBackingProperties]; - if(img) - resizeimg(); + +- (void)resetCursorRects { + [super resetCursorRects]; + [self addCursorRect:self.bounds cursor:self.currentCursor]; } // conforms to protocol NSTextInputClient -- (BOOL)hasMarkedText -{ - LOG(@"hasMarkedText"); - return _markedRange.location != NSNotFound; -} -- (NSRange)markedRange -{ - LOG(@"markedRange"); - return _markedRange; -} -- (NSRange)selectedRange -{ - LOG(@"selectedRange"); - return _selectedRange; -} +- (BOOL)hasMarkedText { return _markedRange.location != NSNotFound; } +- (NSRange)markedRange { return _markedRange; } +- (NSRange)selectedRange { return _selectedRange; } + - (void)setMarkedText:(id)string selectedRange:(NSRange)sRange replacementRange:(NSRange)rRange @@ -616,49 +778,50 @@ struct Cursors { LOG(@"text length %ld", _tmpText.length); for(i = 0; i <= _tmpText.length; ++i){ if(i == _markedRange.location) - keystroke('['); + gfx_keystroke(self.client, '['); if(_selectedRange.length){ if(i == _selectedRange.location) - keystroke('{'); + gfx_keystroke(self.client, '{'); if(i == NSMaxRange(_selectedRange)) - keystroke('}'); + gfx_keystroke(self.client, '}'); } if(i == NSMaxRange(_markedRange)) - keystroke(']'); + gfx_keystroke(self.client, ']'); if(i < _tmpText.length) - keystroke([_tmpText characterAtIndex:i]); + gfx_keystroke(self.client, [_tmpText characterAtIndex:i]); } int l; l = 1 + _tmpText.length - NSMaxRange(_selectedRange) + (_selectedRange.length > 0); LOG(@"move left %d", l); for(i = 0; i < l; ++i) - keystroke(Kleft); + gfx_keystroke(self.client, Kleft); } LOG(@"text: \"%@\" (%ld,%ld) (%ld,%ld)", _tmpText, _markedRange.location, _markedRange.length, _selectedRange.location, _selectedRange.length); } -- (void)unmarkText -{ + +- (void)unmarkText { //NSUInteger i; NSUInteger len; LOG(@"unmarkText"); len = [_tmpText length]; //for(i = 0; i < len; ++i) - // keystroke([_tmpText characterAtIndex:i]); + // gfx_keystroke(self.client, [_tmpText characterAtIndex:i]); [_tmpText deleteCharactersInRange:NSMakeRange(0, len)]; _markedRange = NSMakeRange(NSNotFound, 0); _selectedRange = NSMakeRange(0, 0); } -- (NSArray<NSAttributedStringKey> *)validAttributesForMarkedText -{ + +- (NSArray<NSAttributedStringKey>*)validAttributesForMarkedText { LOG(@"validAttributesForMarkedText"); return @[]; } -- (NSAttributedString *)attributedSubstringForProposedRange:(NSRange)r + +- (NSAttributedString*)attributedSubstringForProposedRange:(NSRange)r actualRange:(NSRangePointer)actualRange { NSRange sr; @@ -671,15 +834,15 @@ struct Cursors { if(actualRange) *actualRange = sr; LOG(@"use range: %ld, %ld", sr.location, sr.length); + s = nil; if(sr.length) s = [[NSAttributedString alloc] initWithString:[_tmpText substringWithRange:sr]]; LOG(@" return %@", s); return s; } -- (void)insertText:(id)s - replacementRange:(NSRange)r -{ + +- (void)insertText:(id)s replacementRange:(NSRange)r { NSUInteger i; NSUInteger len; @@ -689,27 +852,27 @@ struct Cursors { len = [s length]; for(i = 0; i < len; ++i) - keystroke([s characterAtIndex:i]); + gfx_keystroke(self.client, [s characterAtIndex:i]); [_tmpText deleteCharactersInRange:NSMakeRange(0, _tmpText.length)]; _markedRange = NSMakeRange(NSNotFound, 0); _selectedRange = NSMakeRange(0, 0); } + - (NSUInteger)characterIndexForPoint:(NSPoint)point { LOG(@"characterIndexForPoint: %g, %g", point.x, point.y); return 0; } -- (NSRect)firstRectForCharacterRange:(NSRange)r - actualRange:(NSRangePointer)actualRange -{ + +- (NSRect)firstRectForCharacterRange:(NSRange)r actualRange:(NSRangePointer)actualRange { LOG(@"firstRectForCharacterRange: (%ld, %ld) (%ld, %ld)", r.location, r.length, actualRange->location, actualRange->length); if(actualRange) *actualRange = r; return [[self window] convertRectToScreen:_lastInputRect]; } -- (void)doCommandBySelector:(SEL)s -{ + +- (void)doCommandBySelector:(SEL)s { NSEvent *e; NSEventModifierFlags m; uint c, k; @@ -729,12 +892,11 @@ struct Cursors { k += Kcmd; } if(k>0) - keystroke(k); + gfx_keystroke(self.client, k); } // Helper for managing input rect approximately -- (void)resetLastInputRect -{ +- (void)resetLastInputRect { LOG(@"resetLastInputRect"); _lastInputRect.origin.x = 0.0; _lastInputRect.origin.y = 0.0; @@ -742,8 +904,7 @@ struct Cursors { _lastInputRect.size.height = 0.0; } -- (void)enlargeLastInputRect:(NSRect)r -{ +- (void)enlargeLastInputRect:(NSRect)r { r.origin.y = [self bounds].size.height - r.origin.y - r.size.height; _lastInputRect = NSUnionRect(_lastInputRect, r); LOG(@"update last input rect (%g, %g, %g, %g)", @@ -751,8 +912,7 @@ struct Cursors { _lastInputRect.size.width, _lastInputRect.size.height); } -- (void)clearInput -{ +- (void)clearInput { if(_tmpText.length){ uint i; int l; @@ -760,68 +920,35 @@ struct Cursors { + (_selectedRange.length > 0); LOG(@"move right %d", l); for(i = 0; i < l; ++i) - keystroke(Kright); + gfx_keystroke(self.client, Kright); l = _tmpText.length+2+2*(_selectedRange.length > 0); LOG(@"backspace %d", l); for(uint i = 0; i < l; ++i) - keystroke(Kbs); + gfx_keystroke(self.client, Kbs); } } -@end - -@implementation DrawLayer - -- (void)display -{ - id<MTLCommandBuffer> cbuf; - id<MTLBlitCommandEncoder> blit; - - LOG(@"display"); - - cbuf = [commandQueue commandBuffer]; - - LOG(@"display query drawable"); - -@autoreleasepool{ - id<CAMetalDrawable> drawable; - - drawable = [layer nextDrawable]; - if(!drawable){ - LOG(@"display couldn't get drawable"); - [self setNeedsDisplay]; - return; - } - - LOG(@"display got drawable"); - - blit = [cbuf blitCommandEncoder]; - [blit copyFromTexture:texture - sourceSlice:0 - sourceLevel:0 - sourceOrigin:MTLOriginMake(0, 0, 0) - sourceSize:MTLSizeMake(texture.width, texture.height, texture.depth) - toTexture:drawable.texture - destinationSlice:0 - destinationLevel:0 - destinationOrigin:MTLOriginMake(0, 0, 0)]; - [blit endEncoding]; - - [cbuf presentDrawable:drawable]; - drawable = nil; +- (NSApplicationPresentationOptions)window:(id)arg + willUseFullScreenPresentationOptions:(NSApplicationPresentationOptions)proposedOptions { + NSApplicationPresentationOptions o; + o = proposedOptions; + o &= ~(NSApplicationPresentationAutoHideDock | NSApplicationPresentationAutoHideMenuBar); + o |= NSApplicationPresentationHideDock | NSApplicationPresentationHideMenuBar; + return o; } - [cbuf addCompletedHandler:^(id<MTLCommandBuffer> cmdBuff){ - if(cmdBuff.error){ - NSLog(@"command buffer finished with error: %@", - cmdBuff.error.localizedDescription); - }else - LOG(@"command buffer finishes present drawable"); - }]; - [cbuf commit]; - LOG(@"display commit"); +- (void)windowWillEnterFullScreen:(NSNotification*)notification { + // TODO: This should only be done if the window + // is on the screen with the dock. + // But how can you tell which window has the dock? + [[NSApplication sharedApplication] + setPresentationOptions:NSApplicationPresentationHideMenuBar | NSApplicationPresentationHideDock]; } +- (void)windowDidExitFullScreen:(NSNotification*)notification { + [[NSApplication sharedApplication] + setPresentationOptions:NSApplicationPresentationDefault]; +} @end static uint @@ -912,221 +1039,62 @@ keycvt(uint code) } } -Memimage* -attachscreen(char *label, char *winsize) -{ - LOG(@"attachscreen(%s, %s)", label, winsize); - [AppDelegate - performSelectorOnMainThread:@selector(makewin:) - withObject:[NSValue valueWithPointer:winsize] - waitUntilDone:YES]; - kicklabel(label); - setcursor(nil, nil); - mouseresized = 0; - return initimg(); -} - -static Memimage* -initimg(void) -{ -@autoreleasepool{ - CGFloat scale; - NSSize size; - MTLTextureDescriptor *textureDesc; - - size = [myContent convertSizeToBacking:[myContent bounds].size]; - mouserect = Rect(0, 0, size.width, size.height); - - LOG(@"initimg %.0f %.0f", size.width, size.height); - - img = allocmemimage(mouserect, XRGB32); - if(img == nil) - panic("allocmemimage: %r"); - if(img->data == nil) - panic("img->data == nil"); - - textureDesc = [MTLTextureDescriptor - texture2DDescriptorWithPixelFormat:MTLPixelFormatBGRA8Unorm - width:size.width - height:size.height - mipmapped:NO]; - textureDesc.allowGPUOptimizedContents = YES; - textureDesc.usage = MTLTextureUsageShaderRead; - textureDesc.cpuCacheMode = MTLCPUCacheModeWriteCombined; - texture = [device newTextureWithDescriptor:textureDesc]; - - scale = [win backingScaleFactor]; - [layer setDrawableSize:size]; - [layer setContentsScale:scale]; - - // NOTE: This is not really the display DPI. - // On retina, scale is 2; otherwise it is 1. - // This formula gives us 220 for retina, 110 otherwise. - // That's not quite right but it's close to correct. - // https://en.wikipedia.org/wiki/Retina_display#Models - displaydpi = scale * 110; -} - LOG(@"initimg return"); - - return img; -} - -void -_flushmemscreen(Rectangle r) -{ - LOG(@"_flushmemscreen(%d,%d,%d,%d)", r.min.x, r.min.y, Dx(r), Dy(r)); - if(!rectinrect(r, Rect(0, 0, texture.width, texture.height))){ - LOG(@"Rectangle is out of bounds, return."); - return; - } - - @autoreleasepool{ - [texture - replaceRegion:MTLRegionMake2D(r.min.x, r.min.y, Dx(r), Dy(r)) - mipmapLevel:0 - withBytes:byteaddr(img, Pt(r.min.x, r.min.y)) - bytesPerRow:img->width*sizeof(u32int)]; - [AppDelegate - performSelectorOnMainThread:@selector(callsetNeedsDisplayInRect:) - withObject:[NSValue valueWithRect:NSMakeRect(r.min.x, r.min.y, Dx(r), Dy(r))] - waitUntilDone:NO]; - } -} - -void -setmouse(Point p) -{ - @autoreleasepool{ - NSPoint q; - - LOG(@"setmouse(%d,%d)", p.x, p.y); - q = [win convertPointFromBacking:NSMakePoint(p.x, p.y)]; - LOG(@"(%g, %g) <- fromBacking", q.x, q.y); - q = [myContent convertPoint:q toView:nil]; - LOG(@"(%g, %g) <- toWindow", q.x, q.y); - q = [win convertPointToScreen:q]; - LOG(@"(%g, %g) <- toScreen", q.x, q.y); - // Quartz has the origin of the "global display - // coordinate space" at the top left of the primary - // screen with y increasing downward, while Cocoa has - // the origin at the bottom left of the primary screen - // with y increasing upward. We flip the coordinate - // with a negative sign and shift upward by the height - // of the primary screen. - q.y = NSScreen.screens[0].frame.size.height - q.y; - LOG(@"(%g, %g) <- setmouse", q.x, q.y); - CGWarpMouseCursorPosition(NSPointToCGPoint(q)); - CGAssociateMouseAndMouseCursorPosition(true); - } -} - +// rpc_getsnarf reads the current pasteboard as a plain text string. +// Called from an RPC thread with no client lock held. char* -getsnarf(void) +rpc_getsnarf(void) { - NSPasteboard *pb; - NSString *s; - - @autoreleasepool{ - pb = [NSPasteboard generalPasteboard]; - - qlock(&snarfl); - s = [pb stringForType:NSPasteboardTypeString]; - qunlock(&snarfl); - - if(s) - return strdup((char *)[s UTF8String]); - else - return nil; - } -} - -void -putsnarf(char *s) -{ - NSArray *t; - NSPasteboard *pb; - NSString *str; - - if(strlen(s) >= SnarfSize) - return; - - @autoreleasepool{ - t = [NSArray arrayWithObject:NSPasteboardTypeString]; - pb = [NSPasteboard generalPasteboard]; - str = [[NSString alloc] initWithUTF8String:s]; - - qlock(&snarfl); - [pb declareTypes:t owner:nil]; - [pb setString:str forType:NSPasteboardTypeString]; - qunlock(&snarfl); - } + char __block *ret; + + ret = nil; + dispatch_sync(dispatch_get_main_queue(), ^(void) { + @autoreleasepool { + NSPasteboard *pb = [NSPasteboard generalPasteboard]; + NSString *s = [pb stringForType:NSPasteboardTypeString]; + if(s) + ret = strdup((char*)[s UTF8String]); + } + }); + return ret; } +// rpc_putsnarf writes the given text to the pasteboard. +// Called from an RPC thread with no client lock held. void -kicklabel(char *label) +rpc_putsnarf(char *s) { - NSString *s; - - LOG(@"kicklabel(%s)", label); - if(label == nil) + if(s == nil || strlen(s) >= SnarfSize) return; - @autoreleasepool{ - s = [[NSString alloc] initWithUTF8String:label]; - [AppDelegate - performSelectorOnMainThread:@selector(callkicklabel:) - withObject:s - waitUntilDone:NO]; - } -} - -void -setcursor(Cursor *c, Cursor2 *c2) -{ - Cursors cs; - - cs.c = c; - cs.c2 = c2; - - [AppDelegate - performSelectorOnMainThread:@selector(callsetcursor:) - withObject:[NSValue valueWithPointer:&cs] - waitUntilDone:YES]; + dispatch_sync(dispatch_get_main_queue(), ^(void) { + @autoreleasepool{ + NSArray *t = [NSArray arrayWithObject:NSPasteboardTypeString]; + NSPasteboard *pb = [NSPasteboard generalPasteboard]; + NSString *str = [[NSString alloc] initWithUTF8String:s]; + [pb declareTypes:t owner:nil]; + [pb setString:str forType:NSPasteboardTypeString]; + } + }); } +// rpc_bouncemouse is for sending a mouse event +// back to the X11 window manager rio(1). +// Does not apply here. void -topwin(void) +rpc_bouncemouse(Client *c, Mouse m) { - [win - performSelectorOnMainThread: - @selector(makeKeyAndOrderFront:) - withObject:nil - waitUntilDone:YES]; - - [NSApp activateIgnoringOtherApps:YES]; } +// We don't use the graphics thread state during memimagedraw, +// so rpc_gfxdrawlock and rpc_gfxdrawunlock are no-ops. void -resizeimg(void) +rpc_gfxdrawlock(void) { - zlock(); - _drawreplacescreenimage(initimg()); - - mouseresized = 1; - zunlock(); - [myContent sendmouse:0]; } void -resizewindow(Rectangle r) +rpc_gfxdrawunlock(void) { - LOG(@"resizewindow %d %d %d %d", r.min.x, r.min.y, Dx(r), Dy(r)); - dispatch_async(dispatch_get_main_queue(), ^(void){ - NSSize s; - - s = [myContent convertSizeFromBacking:NSMakeSize(Dx(r), Dy(r))]; - [win setContentSize:s]; - }); } static void diff --git a/src/cmd/devdraw/macargv.c b/src/cmd/devdraw/macargv.c deleted file mode 100644 index a5ea1ade..00000000 --- a/src/cmd/devdraw/macargv.c +++ /dev/null @@ -1,90 +0,0 @@ -#include <u.h> -#include <stdio.h> -#include <Carbon/Carbon.h> - -AUTOFRAMEWORK(Carbon) - -static OSErr Handler(const AppleEvent *event, AppleEvent *reply, long handlerRefcon); - -int -main(void) -{ - AEInstallEventHandler(kCoreEventClass, kAEOpenDocuments, Handler, 0, false); - RunApplicationEventLoop(); - return 0; -} - -static OSErr -GetFullPathname(FSSpec *fss, char *path, int len) -{ - FSRef fsr; - OSErr err; - - *path = '\0'; - err = FSpMakeFSRef(fss, &fsr); - if (err == fnfErr) { - /* FSSpecs can point to non-existing files, fsrefs can't. */ - FSSpec fss2; - int tocopy; - - err = FSMakeFSSpec(fss->vRefNum, fss->parID, - (unsigned char*)"", &fss2); - if (err) - return err; - err = FSpMakeFSRef(&fss2, &fsr); - if (err) - return err; - err = (OSErr)FSRefMakePath(&fsr, (unsigned char*)path, len-1); - if (err) - return err; - /* This part is not 100% safe: we append the filename part, but - ** I'm not sure that we don't run afoul of the various 8bit - ** encodings here. Will have to look this up at some point... - */ - strcat(path, "/"); - tocopy = fss->name[0]; - if ((strlen(path) + tocopy) >= len) - tocopy = len - strlen(path) - 1; - if (tocopy > 0) - strncat(path, (char*)fss->name+1, tocopy); - } - else { - if (err) - return err; - err = (OSErr)FSRefMakePath(&fsr, (unsigned char*)path, len); - if (err) - return err; - } - return 0; -} - -static void -chk(int err) -{ - if(err != 0) { - printf("err %d\n", err); - exit(1); - } -} - -static OSErr -Handler(const AppleEvent *event, AppleEvent *reply, long handlerRefcon) -{ - AEDesc list; - DescType type; - FSSpec f; - AEKeyword keyword; - Size actual; - long len; - char s[1000]; - - chk(AEGetParamDesc(event, keyDirectObject, typeAEList, &list)); - chk(AECountItems(&list, &len)); - chk(AEGetNthPtr(&list, 1, typeFSS, &keyword, &type, (Ptr*)&f, sizeof(FSSpec), &actual)); - chk(GetFullPathname(&f, s, sizeof s)); - printf("%s\n", s); - fflush(stdout); - - // uncomment to keep handling more open events - exit(0); -} diff --git a/src/cmd/devdraw/macargv.m b/src/cmd/devdraw/macargv.m index 8db56be7..92df2a6f 100644 --- a/src/cmd/devdraw/macargv.m +++ b/src/cmd/devdraw/macargv.m @@ -12,9 +12,6 @@ AUTOFRAMEWORK(Cocoa) void main(void) { - if(OSX_VERSION < 100700) - [NSAutoreleasePool new]; - [NSApplication sharedApplication]; NSObject<NSApplicationDelegate> *delegate = [appdelegate new]; [NSApp setDelegate:delegate]; diff --git a/src/cmd/devdraw/mkfile b/src/cmd/devdraw/mkfile index 7f0c2a20..6bcf1890 100644 --- a/src/cmd/devdraw/mkfile +++ b/src/cmd/devdraw/mkfile @@ -9,6 +9,7 @@ WSYSOFILES=\ devdraw.$O\ latin1.$O\ mouseswap.$O\ + srv.$O\ winsize.$O\ <|sh ./mkwsysrules.sh @@ -16,14 +17,15 @@ WSYSOFILES=\ OFILES=$WSYSOFILES HFILES=\ + bigarrow.h\ devdraw.h\ + glendapng.h\ + latin1.h\ + $WSYSHFILES\ <$PLAN9/src/mkone -$O.drawclient: drawclient.$O drawfcall.$O - $LD -o $target $prereq - -$O.snarf: x11-alloc.$O x11-cload.$O x11-draw.$O x11-fill.$O x11-get.$O x11-init.$O x11-itrans.$O x11-keysym2ucs.$O x11-load.$O x11-pixelbits.$O x11-unload.$O x11-wsys.$O snarf.$O latin1.$O devdraw.$O +$O.drawclient: drawclient.$O $LD -o $target $prereq $O.mklatinkbd: mklatinkbd.$O @@ -37,16 +39,13 @@ latin1.h: $PLAN9/lib/keyboard $O.mklatinkbd $O.macargv: $MACARGV $LD -o $target $prereq -cocoa-screen-metal-objc.$O: cocoa-screen-metal.m - $CC $CFLAGS $OBJCFLAGS -o $target cocoa-screen-metal.m - -%-objc.$O: %.m - $CC $CFLAGS -o $target $stem.m +%.$O: %.m + $CC $CFLAGS $OBJCFLAGS -fobjc-arc -o $target $stem.m -CLEANFILES=$O.macargv $O.mklatinkbd latin1.h +CLEANFILES=$O.devdraw $O.macargv $O.drawclient $O.mklatinkbd latin1.h install: mklatinkbd.install -install:Q: +install:Q: if [ $MACARGV ]; then mk $MKFLAGS macargv.install fi diff --git a/src/cmd/devdraw/mklatinkbd.c b/src/cmd/devdraw/mklatinkbd.c index abd202fb..db34b6ec 100644 --- a/src/cmd/devdraw/mklatinkbd.c +++ b/src/cmd/devdraw/mklatinkbd.c @@ -132,7 +132,7 @@ printtrie(Biobuf *b, Trie *t) printtrie(b, t->link[i]); if(t->n == 0) return; - + if(xflag) { for(i=0; i<256; i++) { if(t->r[i] == 0) @@ -142,7 +142,7 @@ printtrie(Biobuf *b, Trie *t) Bprint(b, " %k", *p); Bprint(b, " %k : \"%C\" U%04X\n", i, t->r[i], t->r[i]); } - return; + return; } Bprint(b, "\t\""); @@ -315,5 +315,3 @@ kfmt(Fmt *f) return fmtprint(f, "<%s>", xkey[i].s); return fmtprint(f, "<%c>", c); } - - diff --git a/src/cmd/devdraw/mkwsysrules.sh b/src/cmd/devdraw/mkwsysrules.sh index e94afbd3..122e9123 100644 --- a/src/cmd/devdraw/mkwsysrules.sh +++ b/src/cmd/devdraw/mkwsysrules.sh @@ -22,13 +22,11 @@ fi if [ "x$WSYSTYPE" = "x" ]; then if [ "x`uname`" = "xDarwin" ]; then - if sw_vers | grep 'ProductVersion: 10\.[0-5]\.' >/dev/null; then - echo 1>&2 'OS X 10.5 and older are not supported' + if sw_vers | egrep 'ProductVersion: (10\.[0-9]\.|10\.1[012])$' >/dev/null; then + echo 1>&2 'OS X 10.12 and older are not supported' exit 1 - else - #echo 1>&2 'WARNING: OS X Lion is not working. Copy binaries from a Snow Leopard system.' - WSYSTYPE=osx-cocoa fi + WSYSTYPE=mac elif [ -d "$X11" ]; then WSYSTYPE=x11 else @@ -53,14 +51,11 @@ if [ $WSYSTYPE = x11 ]; then echo 'HFILES=$HFILES $XHFILES' XO=`ls x11-*.c 2>/dev/null | sed 's/\.c$/.o/'` echo 'WSYSOFILES=$WSYSOFILES '$XO -elif [ $WSYSTYPE = osx-cocoa ]; then - if sw_vers|awk '/ProductVersion/{split($2,a,".");exit(a[2]<14)}' >/dev/null; then # 0 is true in sh. - echo 'OBJCFLAGS=$OBJCFLAGS -fobjc-arc' - echo 'WSYSOFILES=$WSYSOFILES osx-draw.o cocoa-screen-metal-objc.o cocoa-srv.o cocoa-thread.o' - else - echo 'WSYSOFILES=$WSYSOFILES osx-draw.o cocoa-screen-objc.o cocoa-srv.o cocoa-thread.o' - fi - echo 'MACARGV=macargv-objc.o' + echo 'WSYSHFILES=x11-inc.h x11-keysym2ucs.h x11-memdraw.h' +elif [ $WSYSTYPE = mac ]; then + echo 'WSYSOFILES=$WSYSOFILES mac-draw.o mac-screen.o' + echo 'WSYSHFILES=' + echo 'MACARGV=macargv.o' elif [ $WSYSTYPE = nowsys ]; then echo 'WSYSOFILES=nowsys.o' fi diff --git a/src/cmd/devdraw/mouseswap.c b/src/cmd/devdraw/mouseswap.c index e6ece333..d2dea666 100644 --- a/src/cmd/devdraw/mouseswap.c +++ b/src/cmd/devdraw/mouseswap.c @@ -2,6 +2,11 @@ #include <libc.h> #include <draw.h> #include <memdraw.h> +#include <memlayer.h> +#include <mouse.h> +#include <cursor.h> +#include <keyboard.h> +#include <drawfcall.h> #include "devdraw.h" enum @@ -51,7 +56,7 @@ mouseswap(int but) if(!map.init) initmap(); - + nbut = 0; for(i=0; i<Nbutton; i++) if((but&(1<<i)) && map.b[i] >= 0) diff --git a/src/cmd/devdraw/nowsys.c b/src/cmd/devdraw/nowsys.c index fd8b7ee1..08e0521f 100644 --- a/src/cmd/devdraw/nowsys.c +++ b/src/cmd/devdraw/nowsys.c @@ -18,14 +18,14 @@ main(int argc, char **argv) int n; uchar buf[1024*1024]; Wsysmsg m; - + ARGBEGIN{ case 'D': break; default: usage(); }ARGEND - + if(argc != 0) usage(); diff --git a/src/cmd/devdraw/osx-keycodes.h b/src/cmd/devdraw/osx-keycodes.h deleted file mode 100644 index 52328ace..00000000 --- a/src/cmd/devdraw/osx-keycodes.h +++ /dev/null @@ -1,189 +0,0 @@ -/* These are the Macintosh key scancode constants -- from Inside Macintosh */ -#define QZ_ESCAPE 0x35 -#define QZ_F1 0x7A -#define QZ_F2 0x78 -#define QZ_F3 0x63 -#define QZ_F4 0x76 -#define QZ_F5 0x60 -#define QZ_F6 0x61 -#define QZ_F7 0x62 -#define QZ_F8 0x64 -#define QZ_F9 0x65 -#define QZ_F10 0x6D -#define QZ_F11 0x67 -#define QZ_F12 0x6F -#define QZ_PRINT 0x69 -#define QZ_SCROLLOCK 0x6B -#define QZ_PAUSE 0x71 -#define QZ_POWER 0x7F -#define QZ_BACKQUOTE 0x32 -#define QZ_1 0x12 -#define QZ_2 0x13 -#define QZ_3 0x14 -#define QZ_4 0x15 -#define QZ_5 0x17 -#define QZ_6 0x16 -#define QZ_7 0x1A -#define QZ_8 0x1C -#define QZ_9 0x19 -#define QZ_0 0x1D -#define QZ_MINUS 0x1B -#define QZ_EQUALS 0x18 -#define QZ_BACKSPACE 0x33 -#define QZ_INSERT 0x72 -#define QZ_HOME 0x73 -#define QZ_PAGEUP 0x74 -#define QZ_NUMLOCK 0x47 -#define QZ_KP_EQUALS 0x51 -#define QZ_KP_DIVIDE 0x4B -#define QZ_KP_MULTIPLY 0x43 -#define QZ_TAB 0x30 -#define QZ_q 0x0C -#define QZ_w 0x0D -#define QZ_e 0x0E -#define QZ_r 0x0F -#define QZ_t 0x11 -#define QZ_y 0x10 -#define QZ_u 0x20 -#define QZ_i 0x22 -#define QZ_o 0x1F -#define QZ_p 0x23 -#define QZ_LEFTBRACKET 0x21 -#define QZ_RIGHTBRACKET 0x1E -#define QZ_BACKSLASH 0x2A -#define QZ_DELETE 0x75 -#define QZ_END 0x77 -#define QZ_PAGEDOWN 0x79 -#define QZ_KP7 0x59 -#define QZ_KP8 0x5B -#define QZ_KP9 0x5C -#define QZ_KP_MINUS 0x4E -#define QZ_CAPSLOCK 0x39 -#define QZ_a 0x00 -#define QZ_s 0x01 -#define QZ_d 0x02 -#define QZ_f 0x03 -#define QZ_g 0x05 -#define QZ_h 0x04 -#define QZ_j 0x26 -#define QZ_k 0x28 -#define QZ_l 0x25 -#define QZ_SEMICOLON 0x29 -#define QZ_QUOTE 0x27 -#define QZ_RETURN 0x24 -#define QZ_KP4 0x56 -#define QZ_KP5 0x57 -#define QZ_KP6 0x58 -#define QZ_KP_PLUS 0x45 -#define QZ_LSHIFT 0x38 -#define QZ_z 0x06 -#define QZ_x 0x07 -#define QZ_c 0x08 -#define QZ_v 0x09 -#define QZ_b 0x0B -#define QZ_n 0x2D -#define QZ_m 0x2E -#define QZ_COMMA 0x2B -#define QZ_PERIOD 0x2F -#define QZ_SLASH 0x2C -/* These are the same as the left versions - use left by default */ -#if 0 -#define QZ_RSHIFT 0x38 -#endif -#define QZ_UP 0x7E -#define QZ_KP1 0x53 -#define QZ_KP2 0x54 -#define QZ_KP3 0x55 -#define QZ_KP_ENTER 0x4C -#define QZ_LCTRL 0x3B -#define QZ_LALT 0x3A -#define QZ_LMETA 0x37 -#define QZ_SPACE 0x31 -/* These are the same as the left versions - use left by default */ -#if 0 -#define QZ_RMETA 0x37 -#define QZ_RALT 0x3A -#define QZ_RCTRL 0x3B -#endif -#define QZ_LEFT 0x7B -#define QZ_DOWN 0x7D -#define QZ_RIGHT 0x7C -#define QZ_KP0 0x52 -#define QZ_KP_PERIOD 0x41 - -/* Wierd, these keys are on my iBook under MacOS X */ -#define QZ_IBOOK_ENTER 0x34 -#define QZ_IBOOK_LEFT 0x3B -#define QZ_IBOOK_RIGHT 0x3C -#define QZ_IBOOK_DOWN 0x3D -#define QZ_IBOOK_UP 0x3E -#define KEY_ENTER 13 -#define KEY_TAB 9 - -#define KEY_BASE 0x100 - -/* Function keys */ -#define KEY_F (KEY_BASE+64) - -/* Control keys */ -#define KEY_CTRL (KEY_BASE) -#define KEY_BACKSPACE (KEY_CTRL+0) -#define KEY_DELETE (KEY_CTRL+1) -#define KEY_INSERT (KEY_CTRL+2) -#define KEY_HOME (KEY_CTRL+3) -#define KEY_END (KEY_CTRL+4) -#define KEY_PAGE_UP (KEY_CTRL+5) -#define KEY_PAGE_DOWN (KEY_CTRL+6) -#define KEY_ESC (KEY_CTRL+7) - -/* Control keys short name */ -#define KEY_BS KEY_BACKSPACE -#define KEY_DEL KEY_DELETE -#define KEY_INS KEY_INSERT -#define KEY_PGUP KEY_PAGE_UP -#define KEY_PGDOWN KEY_PAGE_DOWN -#define KEY_PGDWN KEY_PAGE_DOWN - -/* Cursor movement */ -#define KEY_CRSR (KEY_BASE+16) -#define KEY_RIGHT (KEY_CRSR+0) -#define KEY_LEFT (KEY_CRSR+1) -#define KEY_DOWN (KEY_CRSR+2) -#define KEY_UP (KEY_CRSR+3) - -/* Multimedia keyboard/remote keys */ -#define KEY_MM_BASE (0x100+384) -#define KEY_POWER (KEY_MM_BASE+0) -#define KEY_MENU (KEY_MM_BASE+1) -#define KEY_PLAY (KEY_MM_BASE+2) -#define KEY_PAUSE (KEY_MM_BASE+3) -#define KEY_PLAYPAUSE (KEY_MM_BASE+4) -#define KEY_STOP (KEY_MM_BASE+5) -#define KEY_FORWARD (KEY_MM_BASE+6) -#define KEY_REWIND (KEY_MM_BASE+7) -#define KEY_NEXT (KEY_MM_BASE+8) -#define KEY_PREV (KEY_MM_BASE+9) -#define KEY_VOLUME_UP (KEY_MM_BASE+10) -#define KEY_VOLUME_DOWN (KEY_MM_BASE+11) -#define KEY_MUTE (KEY_MM_BASE+12) - -/* Keypad keys */ -#define KEY_KEYPAD (KEY_BASE+32) -#define KEY_KP0 (KEY_KEYPAD+0) -#define KEY_KP1 (KEY_KEYPAD+1) -#define KEY_KP2 (KEY_KEYPAD+2) -#define KEY_KP3 (KEY_KEYPAD+3) -#define KEY_KP4 (KEY_KEYPAD+4) -#define KEY_KP5 (KEY_KEYPAD+5) -#define KEY_KP6 (KEY_KEYPAD+6) -#define KEY_KP7 (KEY_KEYPAD+7) -#define KEY_KP8 (KEY_KEYPAD+8) -#define KEY_KP9 (KEY_KEYPAD+9) -#define KEY_KPDEC (KEY_KEYPAD+10) -#define KEY_KPINS (KEY_KEYPAD+11) -#define KEY_KPDEL (KEY_KEYPAD+12) -#define KEY_KPENTER (KEY_KEYPAD+13) - -/* Special keys */ -#define KEY_INTERN (0x1000) -#define KEY_CLOSE_WIN (KEY_INTERN+0) diff --git a/src/cmd/devdraw/snarf.c b/src/cmd/devdraw/snarf.c deleted file mode 100644 index 1e7a93a1..00000000 --- a/src/cmd/devdraw/snarf.c +++ /dev/null @@ -1,110 +0,0 @@ -#include <u.h> -#include <sys/select.h> -#include <errno.h> -#include "x11-inc.h" -#include <libc.h> -#include <draw.h> -#include <memdraw.h> -#include <memlayer.h> -#include <keyboard.h> -#include <mouse.h> -#include <cursor.h> -#include <drawfcall.h> -#include "x11-memdraw.h" -#include "devdraw.h" - -#undef time - -#define MouseMask (\ - ButtonPressMask|\ - ButtonReleaseMask|\ - PointerMotionMask|\ - Button1MotionMask|\ - Button2MotionMask|\ - Button3MotionMask) - -#define Mask MouseMask|ExposureMask|StructureNotifyMask|KeyPressMask|EnterWindowMask|LeaveWindowMask - -void runxevent(XEvent*); - -void -usage(void) -{ - fprint(2, "usage: snarf [-a] [-o | text]\n"); - exits("usage"); -} - -void -main(int argc, char **argv) -{ - int apple; - int out; - - apple = 0; - out = 0; - - ARGBEGIN{ - case 'a': - apple = 1; - break; - case 'o': - out = 1; - break; - default: - usage(); - }ARGEND - - if(out && argc != 0) - usage(); - if(!out && argc != 1) - usage(); - - _x.fd = -1; - - memimageinit(); - _xattach("snarf", "20x20"); - - XSelectInput(_x.display, _x.drawable, Mask); - XFlush(_x.display); - - if(out){ - char *s; - if(apple) - s = _applegetsnarf(); - else - s = _xgetsnarf(); - write(1, s, strlen(s)); - write(1, "\n", 1); - exits(0); - }else{ - _xputsnarf(argv[0]); - for(;;){ - XEvent event; - XNextEvent(_x.display, &event); - runxevent(&event); - } - } -} - -/* - * Handle an incoming X event. - */ -void -runxevent(XEvent *xev) -{ - switch(xev->type){ - case Expose: - _xexpose(xev); - break; - - case DestroyNotify: - if(_xdestroy(xev)) - exits(0); - break; - - case SelectionRequest: - _xselect(xev); - break; - } -} - diff --git a/src/cmd/devdraw/srv.c b/src/cmd/devdraw/srv.c new file mode 100644 index 00000000..bfeb7c38 --- /dev/null +++ b/src/cmd/devdraw/srv.c @@ -0,0 +1,521 @@ +/* + * Window system protocol server. + */ + +#include <u.h> +#include <libc.h> +#include <thread.h> +#include <draw.h> +#include <memdraw.h> +#include <memlayer.h> +#include <keyboard.h> +#include <mouse.h> +#include <cursor.h> +#include <drawfcall.h> +#include "devdraw.h" + +static void runmsg(Client*, Wsysmsg*); +static void replymsg(Client*, Wsysmsg*); +static void matchkbd(Client*); +static void matchmouse(Client*); +static void serveproc(void*); +static void listenproc(void*); +Client *client0; + +int trace = 0; +static char *srvname; +static int afd; +static char adir[40]; + +static void +usage(void) +{ + fprint(2, "usage: devdraw (don't run directly)\n"); + threadexitsall("usage"); +} + +void +threadmain(int argc, char **argv) +{ + ARGBEGIN{ + case 'D': /* for good ps -a listings */ + break; + case 'f': /* fall through for backward compatibility */ + case 'g': + case 'b': + break; + case 's': + // TODO: Update usage, man page. + srvname = EARGF(usage()); + break; + default: + usage(); + }ARGEND + + if(srvname == nil) { + client0 = mallocz(sizeof(Client), 1); + if(client0 == nil){ + fprint(2, "initdraw: allocating client0: out of memory"); + abort(); + } + client0->displaydpi = 100; + client0->rfd = 3; + client0->wfd = 4; + + /* + * Move the protocol off stdin/stdout so that + * any inadvertent prints don't screw things up. + */ + dup(0,3); + dup(1,4); + close(0); + close(1); + open("/dev/null", OREAD); + open("/dev/null", OWRITE); + } + + fmtinstall('W', drawfcallfmt); + gfx_main(); +} + +void +gfx_started(void) +{ + char *addr; + + if(srvname == nil) { + // Legacy mode: serving single client on pipes. + proccreate(serveproc, client0, 0); + return; + } + + // Server mode. + addr = smprint("unix!%s/%s", getns(), srvname); + if(addr == nil) + sysfatal("out of memory"); + + if((afd = announce(addr, adir)) < 0) + sysfatal("announce %s: %r", addr); + + proccreate(listenproc, nil, 0); +} + +static void +listenproc(void *v) +{ + Client *c; + int fd; + char dir[40]; + + USED(v); + + for(;;) { + fd = listen(adir, dir); + if(fd < 0) + sysfatal("listen: %r"); + c = mallocz(sizeof(Client), 1); + if(c == nil){ + fprint(2, "initdraw: allocating client0: out of memory"); + abort(); + } + c->displaydpi = 100; + c->rfd = fd; + c->wfd = fd; + proccreate(serveproc, c, 0); + } +} + +static void +serveproc(void *v) +{ + Client *c; + uchar buf[4], *mbuf; + int nmbuf, n, nn; + Wsysmsg m; + + c = v; + mbuf = nil; + nmbuf = 0; + while((n = read(c->rfd, buf, 4)) == 4){ + GET(buf, n); + if(n > nmbuf){ + free(mbuf); + mbuf = malloc(4+n); + if(mbuf == nil) + sysfatal("out of memory"); + nmbuf = n; + } + memmove(mbuf, buf, 4); + nn = readn(c->rfd, mbuf+4, n-4); + if(nn != n-4) { + fprint(2, "serveproc: eof during message\n"); + break; + } + + /* pick off messages one by one */ + if(convM2W(mbuf, nn+4, &m) <= 0) { + fprint(2, "serveproc: cannot convert message\n"); + break; + } + if(trace) fprint(2, "%ud [%d] <- %W\n", nsec()/1000000, threadid(), &m); + runmsg(c, &m); + } + + if(c == client0) { + rpc_shutdown(); + threadexitsall(nil); + } +} + +static void +replyerror(Client *c, Wsysmsg *m) +{ + char err[256]; + + rerrstr(err, sizeof err); + m->type = Rerror; + m->error = err; + replymsg(c, m); +} + +/* + * Handle a single wsysmsg. + * Might queue for later (kbd, mouse read) + */ +static void +runmsg(Client *c, Wsysmsg *m) +{ + static uchar buf[65536]; + int n; + Memimage *i; + + switch(m->type){ + case Tctxt: + c->wsysid = strdup(m->id); + replymsg(c, m); + break; + + case Tinit: + memimageinit(); + i = rpc_attach(c, m->label, m->winsize); + draw_initdisplaymemimage(c, i); + replymsg(c, m); + break; + + case Trdmouse: + qlock(&c->eventlk); + if((c->mousetags.wi+1)%nelem(c->mousetags.t) == c->mousetags.ri) { + qunlock(&c->eventlk); + werrstr("too many queued mouse reads"); + replyerror(c, m); + break; + } + c->mousetags.t[c->mousetags.wi++] = m->tag; + if(c->mousetags.wi == nelem(c->mousetags.t)) + c->mousetags.wi = 0; + c->mouse.stall = 0; + matchmouse(c); + qunlock(&c->eventlk); + break; + + case Trdkbd: + qlock(&c->eventlk); + if((c->kbdtags.wi+1)%nelem(c->kbdtags.t) == c->kbdtags.ri) { + qunlock(&c->eventlk); + werrstr("too many queued keyboard reads"); + replyerror(c, m); + break; + } + c->kbdtags.t[c->kbdtags.wi++] = m->tag; + if(c->kbdtags.wi == nelem(c->kbdtags.t)) + c->kbdtags.wi = 0; + c->kbd.stall = 0; + matchkbd(c); + qunlock(&c->eventlk); + break; + + case Tmoveto: + rpc_setmouse(c, m->mouse.xy); + replymsg(c, m); + break; + + case Tcursor: + if(m->arrowcursor) + rpc_setcursor(c, nil, nil); + else { + scalecursor(&m->cursor2, &m->cursor); + rpc_setcursor(c, &m->cursor, &m->cursor2); + } + replymsg(c, m); + break; + + case Tcursor2: + if(m->arrowcursor) + rpc_setcursor(c, nil, nil); + else + rpc_setcursor(c, &m->cursor, &m->cursor2); + replymsg(c, m); + break; + + case Tbouncemouse: + rpc_bouncemouse(c, m->mouse); + replymsg(c, m); + break; + + case Tlabel: + rpc_setlabel(c, m->label); + replymsg(c, m); + break; + + case Trdsnarf: + m->snarf = rpc_getsnarf(); + replymsg(c, m); + free(m->snarf); + break; + + case Twrsnarf: + rpc_putsnarf(m->snarf); + replymsg(c, m); + break; + + case Trddraw: + n = m->count; + if(n > sizeof buf) + n = sizeof buf; + n = draw_dataread(c, buf, n); + if(n < 0) + replyerror(c, m); + else{ + m->count = n; + m->data = buf; + replymsg(c, m); + } + break; + + case Twrdraw: + if(draw_datawrite(c, m->data, m->count) < 0) + replyerror(c, m); + else + replymsg(c, m); + break; + + case Ttop: + rpc_topwin(c); + replymsg(c, m); + break; + + case Tresize: + rpc_resizewindow(c, m->rect); + replymsg(c, m); + break; + } +} + +/* + * Reply to m. + */ +static void +replymsg(Client *c, Wsysmsg *m) +{ + int n; + + /* T -> R msg */ + if(m->type%2 == 0) + m->type++; + + if(trace) fprint(2, "%ud [%d] -> %W\n", nsec()/1000000, threadid(), m); + /* copy to output buffer */ + n = sizeW2M(m); + + qlock(&c->wfdlk); + if(n > c->nmbuf){ + free(c->mbuf); + c->mbuf = malloc(n); + if(c->mbuf == nil) + sysfatal("out of memory"); + c->nmbuf = n; + } + convW2M(m, c->mbuf, n); + if(write(c->wfd, c->mbuf, n) != n) + fprint(2, "client write: %r\n"); + qunlock(&c->wfdlk); +} + +/* + * Match queued kbd reads with queued kbd characters. + */ +static void +matchkbd(Client *c) +{ + Wsysmsg m; + + if(c->kbd.stall) + return; + while(c->kbd.ri != c->kbd.wi && c->kbdtags.ri != c->kbdtags.wi){ + m.type = Rrdkbd; + m.tag = c->kbdtags.t[c->kbdtags.ri++]; + if(c->kbdtags.ri == nelem(c->kbdtags.t)) + c->kbdtags.ri = 0; + m.rune = c->kbd.r[c->kbd.ri++]; + if(c->kbd.ri == nelem(c->kbd.r)) + c->kbd.ri = 0; + replymsg(c, &m); + } +} + +// matchmouse matches queued mouse reads with queued mouse events. +// It must be called with c->eventlk held. +static void +matchmouse(Client *c) +{ + Wsysmsg m; + + if(canqlock(&c->eventlk)) { + fprint(2, "misuse of matchmouse\n"); + abort(); + } + + while(c->mouse.ri != c->mouse.wi && c->mousetags.ri != c->mousetags.wi){ + m.type = Rrdmouse; + m.tag = c->mousetags.t[c->mousetags.ri++]; + if(c->mousetags.ri == nelem(c->mousetags.t)) + c->mousetags.ri = 0; + m.mouse = c->mouse.m[c->mouse.ri]; + m.resized = c->mouse.resized; + c->mouse.resized = 0; + /* + if(m.resized) + fprint(2, "sending resize\n"); + */ + c->mouse.ri++; + if(c->mouse.ri == nelem(c->mouse.m)) + c->mouse.ri = 0; + replymsg(c, &m); + } +} + +void +gfx_mousetrack(Client *c, int x, int y, int b, uint ms) +{ + Mouse *m; + + qlock(&c->eventlk); + if(x < c->mouserect.min.x) + x = c->mouserect.min.x; + if(x > c->mouserect.max.x) + x = c->mouserect.max.x; + if(y < c->mouserect.min.y) + y = c->mouserect.min.y; + if(y > c->mouserect.max.y) + y = c->mouserect.max.y; + + // If reader has stopped reading, don't bother. + // If reader is completely caught up, definitely queue. + // Otherwise, queue only button change events. + if(!c->mouse.stall) + if(c->mouse.wi == c->mouse.ri || c->mouse.last.buttons != b){ + m = &c->mouse.last; + m->xy.x = x; + m->xy.y = y; + m->buttons = b; + m->msec = ms; + + c->mouse.m[c->mouse.wi] = *m; + if(++c->mouse.wi == nelem(c->mouse.m)) + c->mouse.wi = 0; + if(c->mouse.wi == c->mouse.ri){ + c->mouse.stall = 1; + c->mouse.ri = 0; + c->mouse.wi = 1; + c->mouse.m[0] = *m; + } + matchmouse(c); + } + qunlock(&c->eventlk); +} + +// kputc adds ch to the keyboard buffer. +// It must be called with c->eventlk held. +static void +kputc(Client *c, int ch) +{ + if(canqlock(&c->eventlk)) { + fprint(2, "misuse of kputc\n"); + abort(); + } + + c->kbd.r[c->kbd.wi++] = ch; + if(c->kbd.wi == nelem(c->kbd.r)) + c->kbd.wi = 0; + if(c->kbd.ri == c->kbd.wi) + c->kbd.stall = 1; + matchkbd(c); +} + +// gfx_abortcompose stops any pending compose sequence, +// because a mouse button has been clicked. +// It is called from the graphics thread with no locks held. +void +gfx_abortcompose(Client *c) +{ + qlock(&c->eventlk); + if(c->kbd.alting) { + c->kbd.alting = 0; + c->kbd.nk = 0; + } + qunlock(&c->eventlk); +} + +// gfx_keystroke records a single-rune keystroke. +// It is called from the graphics thread with no locks held. +void +gfx_keystroke(Client *c, int ch) +{ + int i; + + qlock(&c->eventlk); + if(ch == Kalt){ + c->kbd.alting = !c->kbd.alting; + c->kbd.nk = 0; + qunlock(&c->eventlk); + return; + } + if(ch == Kcmd+'r') { + if(c->forcedpi) + c->forcedpi = 0; + else if(c->displaydpi >= 200) + c->forcedpi = 100; + else + c->forcedpi = 225; + qunlock(&c->eventlk); + rpc_resizeimg(c); + return; + } + if(!c->kbd.alting){ + kputc(c, ch); + qunlock(&c->eventlk); + return; + } + if(c->kbd.nk >= nelem(c->kbd.k)) // should not happen + c->kbd.nk = 0; + c->kbd.k[c->kbd.nk++] = ch; + ch = latin1(c->kbd.k, c->kbd.nk); + if(ch > 0){ + c->kbd.alting = 0; + kputc(c, ch); + c->kbd.nk = 0; + qunlock(&c->eventlk); + return; + } + if(ch == -1){ + c->kbd.alting = 0; + for(i=0; i<c->kbd.nk; i++) + kputc(c, c->kbd.k[i]); + c->kbd.nk = 0; + qunlock(&c->eventlk); + return; + } + // need more input + qunlock(&c->eventlk); + return; +} diff --git a/src/cmd/devdraw/winsize.c b/src/cmd/devdraw/winsize.c index 375401bf..7d0a7b70 100644 --- a/src/cmd/devdraw/winsize.c +++ b/src/cmd/devdraw/winsize.c @@ -2,6 +2,11 @@ #include <libc.h> #include <draw.h> #include <memdraw.h> +#include <memlayer.h> +#include <mouse.h> +#include <cursor.h> +#include <keyboard.h> +#include <drawfcall.h> #include "devdraw.h" int diff --git a/src/cmd/devdraw/x11-alloc.c b/src/cmd/devdraw/x11-alloc.c index a465f998..cee93064 100644 --- a/src/cmd/devdraw/x11-alloc.c +++ b/src/cmd/devdraw/x11-alloc.c @@ -21,7 +21,7 @@ _xallocmemimage(Rectangle r, u32int chan, int pixmap) m = _allocmemimage(r, chan); if(chan != GREY1 && chan != _x.chan) return m; - if(_x.display == 0) + if(_x.display == 0 || _x.windows == nil) return m; /* @@ -49,7 +49,7 @@ _xallocmemimage(Rectangle r, u32int chan, int pixmap) if(pixmap != PMundef) xm->pixmap = pixmap; else - xm->pixmap = XCreatePixmap(_x.display, _x.drawable, Dx(r), Dy(r), d); + xm->pixmap = XCreatePixmap(_x.display, _x.windows->drawable, Dx(r), Dy(r), d); /* * We want to align pixels on word boundaries. @@ -121,4 +121,3 @@ freememimage(Memimage *m) } _freememimage(m); } - diff --git a/src/cmd/devdraw/x11-cload.c b/src/cmd/devdraw/x11-cload.c index 33e3170a..1666eced 100644 --- a/src/cmd/devdraw/x11-cload.c +++ b/src/cmd/devdraw/x11-cload.c @@ -15,4 +15,3 @@ cloadmemimage(Memimage *i, Rectangle r, uchar *data, int ndata) _xputxdata(i, r); return n; } - diff --git a/src/cmd/devdraw/x11-draw.c b/src/cmd/devdraw/x11-draw.c index 685ad88a..f3b6a689 100644 --- a/src/cmd/devdraw/x11-draw.c +++ b/src/cmd/devdraw/x11-draw.c @@ -147,4 +147,3 @@ xdraw(Memdrawparam *par) */ return 0; } - diff --git a/src/cmd/devdraw/x11-fill.c b/src/cmd/devdraw/x11-fill.c index fc43a684..adead1ea 100644 --- a/src/cmd/devdraw/x11-fill.c +++ b/src/cmd/devdraw/x11-fill.c @@ -23,7 +23,7 @@ _xfillcolor(Memimage *m, Rectangle r, u32int v) Point p; Xmem *xm; XGC gc; - + xm = m->X; assert(xm != nil); @@ -52,5 +52,3 @@ _xfillcolor(Memimage *m, Rectangle r, u32int v) p = subpt(r.min, m->r.min); XFillRectangle(_x.display, xm->pixmap, gc, p.x, p.y, Dx(r), Dy(r)); } - - diff --git a/src/cmd/devdraw/x11-get.c b/src/cmd/devdraw/x11-get.c index 1a47be01..61913f18 100644 --- a/src/cmd/devdraw/x11-get.c +++ b/src/cmd/devdraw/x11-get.c @@ -21,7 +21,7 @@ _xgetxdata(Memimage *m, Rectangle r) uchar *p; Point tp, xdelta, delta; Xmem *xm; - + xm = m->X; if(xm == nil) return nil; @@ -83,7 +83,7 @@ _xputxdata(Memimage *m, Rectangle r) XPutImage(_x.display, xm->pixmap, gc, xi, xdelta.x, xdelta.y, delta.x, delta.y, Dx(r), Dy(r)); - + if(_x.usetable && m->chan==CMAP8){ for(y=r.min.y; y<r.max.y; y++) for(x=r.min.x, p=byteaddr(m, Pt(x,y)); x<r.max.x; x++, p++) @@ -103,6 +103,3 @@ _xdirtyxdata(Memimage *m, Rectangle r) xm->dirty = 1; addrect(&xm->dirtyr, r); } - - - diff --git a/src/cmd/devdraw/x11-inc.h b/src/cmd/devdraw/x11-inc.h index 1ac27d65..dca3ebcd 100644 --- a/src/cmd/devdraw/x11-inc.h +++ b/src/cmd/devdraw/x11-inc.h @@ -31,5 +31,3 @@ #undef Screen #undef Visual #undef Window - -void sendalt(void); diff --git a/src/cmd/devdraw/x11-init.c b/src/cmd/devdraw/x11-init.c deleted file mode 100644 index f09963dc..00000000 --- a/src/cmd/devdraw/x11-init.c +++ /dev/null @@ -1,737 +0,0 @@ -/* - * Some of the stuff in this file is not X-dependent and should be elsewhere. - */ -#include <u.h> -#include "x11-inc.h" -#include <libc.h> -#include <draw.h> -#include <memdraw.h> -#include <keyboard.h> -#include <mouse.h> -#include <cursor.h> -#include "x11-memdraw.h" -#include "devdraw.h" - -static void plan9cmap(void); -static int setupcmap(XWindow); -static XGC xgc(XDrawable, int, int); - -Xprivate _x; - -static int -xerror(XDisplay *d, XErrorEvent *e) -{ - char buf[200]; - - if(e->request_code == 42) /* XSetInputFocus */ - return 0; - if(e->request_code == 18) /* XChangeProperty */ - return 0; - /* - * BadDrawable happens in apps that get resized a LOT, - * e.g. when KDE is configured to resize continuously - * during a window drag. - */ - if(e->error_code == 9) /* BadDrawable */ - return 0; - - fprint(2, "X error: error_code=%d, request_code=%d, minor=%d disp=%p\n", - e->error_code, e->request_code, e->minor_code, d); - XGetErrorText(d, e->error_code, buf, sizeof buf); - fprint(2, "%s\n", buf); - return 0; -} - -static int -xioerror(XDisplay *d) -{ - /*print("X I/O error\n"); */ - exit(0); - /*sysfatal("X I/O error\n");*/ - abort(); - return -1; -} - - -Memimage* -_xattach(char *label, char *winsize) -{ - char *argv[2], *disp; - int i, havemin, height, mask, n, width, x, xrootid, y; - Rectangle r; - XClassHint classhint; - XDrawable pmid; - XPixmapFormatValues *pfmt; - XScreen *xscreen; - XSetWindowAttributes attr; - XSizeHints normalhint; - XTextProperty name; - XVisualInfo xvi; - XWindow xrootwin; - XWindowAttributes wattr; - XWMHints hint; - Atom atoms[2]; - - /* - if(XInitThreads() == 0){ - fprint(2, "XInitThreads failed\n"); - abort(); - } - */ - - /* - * Connect to X server. - */ - _x.display = XOpenDisplay(NULL); - if(_x.display == nil){ - disp = getenv("DISPLAY"); - werrstr("XOpenDisplay %s: %r", disp ? disp : ":0"); - free(disp); - return nil; - } - _x.fd = ConnectionNumber(_x.display); - XSetErrorHandler(xerror); - XSetIOErrorHandler(xioerror); - xrootid = DefaultScreen(_x.display); - xrootwin = DefaultRootWindow(_x.display); - - /* - * Figure out underlying screen format. - */ - if(XMatchVisualInfo(_x.display, xrootid, 24, TrueColor, &xvi) - || XMatchVisualInfo(_x.display, xrootid, 24, DirectColor, &xvi)){ - _x.vis = xvi.visual; - _x.depth = 24; - } - else - if(XMatchVisualInfo(_x.display, xrootid, 16, TrueColor, &xvi) - || XMatchVisualInfo(_x.display, xrootid, 16, DirectColor, &xvi)){ - _x.vis = xvi.visual; - _x.depth = 16; - } - else - if(XMatchVisualInfo(_x.display, xrootid, 15, TrueColor, &xvi) - || XMatchVisualInfo(_x.display, xrootid, 15, DirectColor, &xvi)){ - _x.vis = xvi.visual; - _x.depth = 15; - } - else - if(XMatchVisualInfo(_x.display, xrootid, 8, PseudoColor, &xvi) - || XMatchVisualInfo(_x.display, xrootid, 8, StaticColor, &xvi)){ - if(_x.depth > 8){ - werrstr("can't deal with colormapped depth %d screens", - _x.depth); - goto err0; - } - _x.vis = xvi.visual; - _x.depth = 8; - } - else{ - _x.depth = DefaultDepth(_x.display, xrootid); - if(_x.depth != 8){ - werrstr("can't understand depth %d screen", _x.depth); - goto err0; - } - _x.vis = DefaultVisual(_x.display, xrootid); - } - - if(DefaultDepth(_x.display, xrootid) == _x.depth) - _x.usetable = 1; - - /* - * _x.depth is only the number of significant pixel bits, - * not the total number of pixel bits. We need to walk the - * display list to find how many actual bits are used - * per pixel. - */ - _x.chan = 0; - pfmt = XListPixmapFormats(_x.display, &n); - for(i=0; i<n; i++){ - if(pfmt[i].depth == _x.depth){ - switch(pfmt[i].bits_per_pixel){ - case 1: /* untested */ - _x.chan = GREY1; - break; - case 2: /* untested */ - _x.chan = GREY2; - break; - case 4: /* untested */ - _x.chan = GREY4; - break; - case 8: - _x.chan = CMAP8; - break; - case 15: - _x.chan = RGB15; - break; - case 16: /* how to tell RGB15? */ - _x.chan = RGB16; - break; - case 24: /* untested (impossible?) */ - _x.chan = RGB24; - break; - case 32: - _x.chan = XRGB32; - break; - } - } - } - XFree(pfmt); - if(_x.chan == 0){ - werrstr("could not determine screen pixel format"); - goto err0; - } - - /* - * Set up color map if necessary. - */ - xscreen = DefaultScreenOfDisplay(_x.display); - _x.cmap = DefaultColormapOfScreen(xscreen); - if(_x.vis->class != StaticColor){ - plan9cmap(); - setupcmap(xrootwin); - } - - /* - * We get to choose the initial rectangle size. - * This is arbitrary. In theory we should read the - * command line and allow the traditional X options. - */ - mask = 0; - x = 0; - y = 0; - if(winsize && winsize[0]){ - if(parsewinsize(winsize, &r, &havemin) < 0) - sysfatal("%r"); - }else{ - /* - * Parse the various X resources. Thanks to Peter Canning. - */ - char *screen_resources, *display_resources, *geom, - *geomrestype, *home, *file, *dpitype; - XrmDatabase database; - XrmValue geomres, dpires; - - database = XrmGetDatabase(_x.display); - screen_resources = XScreenResourceString(xscreen); - if(screen_resources != nil){ - XrmCombineDatabase(XrmGetStringDatabase(screen_resources), &database, False); - XFree(screen_resources); - } - - display_resources = XResourceManagerString(_x.display); - if(display_resources == nil){ - home = getenv("HOME"); - if(home!=nil && (file=smprint("%s/.Xdefaults", home)) != nil){ - XrmCombineFileDatabase(file, &database, False); - free(file); - } - free(home); - }else - XrmCombineDatabase(XrmGetStringDatabase(display_resources), &database, False); - - if (XrmGetResource(database, "Xft.dpi", "String", &dpitype, &dpires) == True) { - if (dpires.addr) { - displaydpi=atoi(dpires.addr); - } - } - geom = smprint("%s.geometry", label); - if(geom && XrmGetResource(database, geom, nil, &geomrestype, &geomres)) - mask = XParseGeometry(geomres.addr, &x, &y, (uint*)&width, (uint*)&height); - XrmDestroyDatabase(database); - free(geom); - - if((mask & WidthValue) && (mask & HeightValue)){ - r = Rect(0, 0, width, height); - }else{ - r = Rect(0, 0, WidthOfScreen(xscreen)*3/4, - HeightOfScreen(xscreen)*3/4); - if(Dx(r) > Dy(r)*3/2) - r.max.x = r.min.x + Dy(r)*3/2; - if(Dy(r) > Dx(r)*3/2) - r.max.y = r.min.y + Dx(r)*3/2; - } - if(mask & XNegative){ - x += WidthOfScreen(xscreen); - } - if(mask & YNegative){ - y += HeightOfScreen(xscreen); - } - havemin = 0; - } - screenrect = Rect(0, 0, WidthOfScreen(xscreen), HeightOfScreen(xscreen)); - windowrect = r; - - memset(&attr, 0, sizeof attr); - attr.colormap = _x.cmap; - attr.background_pixel = ~0; - attr.border_pixel = 0; - _x.drawable = XCreateWindow( - _x.display, /* display */ - xrootwin, /* parent */ - x, /* x */ - y, /* y */ - Dx(r), /* width */ - Dy(r), /* height */ - 0, /* border width */ - _x.depth, /* depth */ - InputOutput, /* class */ - _x.vis, /* visual */ - /* valuemask */ - CWBackPixel|CWBorderPixel|CWColormap, - &attr /* attributes (the above aren't?!) */ - ); - - /* - * Label and other properties required by ICCCCM. - */ - memset(&name, 0, sizeof name); - if(label == nil) - label = "pjw-face-here"; - name.value = (uchar*)label; - name.encoding = XA_STRING; - name.format = 8; - name.nitems = strlen((char*)name.value); - - memset(&normalhint, 0, sizeof normalhint); - normalhint.flags = PSize|PMaxSize; - if(winsize && winsize[0]){ - normalhint.flags &= ~PSize; - normalhint.flags |= USSize; - normalhint.width = Dx(r); - normalhint.height = Dy(r); - }else{ - if((mask & WidthValue) && (mask & HeightValue)){ - normalhint.flags &= ~PSize; - normalhint.flags |= USSize; - normalhint.width = width; - normalhint.height = height; - } - if((mask & WidthValue) && (mask & HeightValue)){ - normalhint.flags |= USPosition; - normalhint.x = x; - normalhint.y = y; - } - } - - normalhint.max_width = WidthOfScreen(xscreen); - normalhint.max_height = HeightOfScreen(xscreen); - - memset(&hint, 0, sizeof hint); - hint.flags = InputHint|StateHint; - hint.input = 1; - hint.initial_state = NormalState; - - memset(&classhint, 0, sizeof classhint); - classhint.res_name = label; - classhint.res_class = label; - - argv[0] = label; - argv[1] = nil; - - XSetWMProperties( - _x.display, /* display */ - _x.drawable, /* window */ - &name, /* XA_WM_NAME property */ - &name, /* XA_WM_ICON_NAME property */ - argv, /* XA_WM_COMMAND */ - 1, /* argc */ - &normalhint, /* XA_WM_NORMAL_HINTS */ - &hint, /* XA_WM_HINTS */ - &classhint /* XA_WM_CLASSHINTS */ - ); - XFlush(_x.display); - - if(havemin){ - XWindowChanges ch; - - memset(&ch, 0, sizeof ch); - ch.x = r.min.x; - ch.y = r.min.y; - XConfigureWindow(_x.display, _x.drawable, CWX|CWY, &ch); - /* - * Must pretend origin is 0,0 for X. - */ - r = Rect(0,0,Dx(r),Dy(r)); - } - /* - * Look up clipboard atom. - */ - _x.clipboard = XInternAtom(_x.display, "CLIPBOARD", False); - _x.utf8string = XInternAtom(_x.display, "UTF8_STRING", False); - _x.targets = XInternAtom(_x.display, "TARGETS", False); - _x.text = XInternAtom(_x.display, "TEXT", False); - _x.compoundtext = XInternAtom(_x.display, "COMPOUND_TEXT", False); - _x.takefocus = XInternAtom(_x.display, "WM_TAKE_FOCUS", False); - _x.losefocus = XInternAtom(_x.display, "_9WM_LOSE_FOCUS", False); - _x.wmprotos = XInternAtom(_x.display, "WM_PROTOCOLS", False); - - atoms[0] = _x.takefocus; - atoms[1] = _x.losefocus; - XChangeProperty(_x.display, _x.drawable, _x.wmprotos, XA_ATOM, 32, - PropModeReplace, (uchar*)atoms, 2); - - /* - * Put the window on the screen, check to see what size we actually got. - */ - XMapWindow(_x.display, _x.drawable); - XSync(_x.display, False); - - if(!XGetWindowAttributes(_x.display, _x.drawable, &wattr)) - fprint(2, "XGetWindowAttributes failed\n"); - else if(wattr.width && wattr.height){ - if(wattr.width != Dx(r) || wattr.height != Dy(r)){ - r.max.x = wattr.width; - r.max.y = wattr.height; - } - }else - fprint(2, "XGetWindowAttributes: bad attrs\n"); - - /* - * Allocate our local backing store. - */ - _x.screenr = r; - _x.screenpm = XCreatePixmap(_x.display, _x.drawable, Dx(r), Dy(r), _x.depth); - _x.nextscreenpm = _x.screenpm; - _x.screenimage = _xallocmemimage(r, _x.chan, _x.screenpm); - - /* - * Allocate some useful graphics contexts for the future. - */ - _x.gcfill = xgc(_x.screenpm, FillSolid, -1); - _x.gccopy = xgc(_x.screenpm, -1, -1); - _x.gcsimplesrc = xgc(_x.screenpm, FillStippled, -1); - _x.gczero = xgc(_x.screenpm, -1, -1); - _x.gcreplsrc = xgc(_x.screenpm, FillTiled, -1); - - pmid = XCreatePixmap(_x.display, _x.drawable, 1, 1, 1); - _x.gcfill0 = xgc(pmid, FillSolid, 0); - _x.gccopy0 = xgc(pmid, -1, -1); - _x.gcsimplesrc0 = xgc(pmid, FillStippled, -1); - _x.gczero0 = xgc(pmid, -1, -1); - _x.gcreplsrc0 = xgc(pmid, FillTiled, -1); - XFreePixmap(_x.display, pmid); - - return _x.screenimage; - -err0: - /* - * Should do a better job of cleaning up here. - */ - XCloseDisplay(_x.display); - return nil; -} - -int -_xsetlabel(char *label) -{ - XTextProperty name; - - /* - * Label and other properties required by ICCCCM. - */ - memset(&name, 0, sizeof name); - if(label == nil) - label = "pjw-face-here"; - name.value = (uchar*)label; - name.encoding = XA_STRING; - name.format = 8; - name.nitems = strlen((char*)name.value); - - XSetWMProperties( - _x.display, /* display */ - _x.drawable, /* window */ - &name, /* XA_WM_NAME property */ - &name, /* XA_WM_ICON_NAME property */ - nil, /* XA_WM_COMMAND */ - 0, /* argc */ - nil, /* XA_WM_NORMAL_HINTS */ - nil, /* XA_WM_HINTS */ - nil /* XA_WM_CLASSHINTS */ - ); - XFlush(_x.display); - return 0; -} - -/* - * Create a GC with a particular fill style and XXX. - * Disable generation of GraphicsExpose/NoExpose events in the GC. - */ -static XGC -xgc(XDrawable d, int fillstyle, int foreground) -{ - XGC gc; - XGCValues v; - - memset(&v, 0, sizeof v); - v.function = GXcopy; - v.graphics_exposures = False; - gc = XCreateGC(_x.display, d, GCFunction|GCGraphicsExposures, &v); - if(fillstyle != -1) - XSetFillStyle(_x.display, gc, fillstyle); - if(foreground != -1) - XSetForeground(_x.display, gc, 0); - return gc; -} - - -/* - * Initialize map with the Plan 9 rgbv color map. - */ -static void -plan9cmap(void) -{ - int r, g, b, cr, cg, cb, v, num, den, idx, v7, idx7; - static int once; - - if(once) - return; - once = 1; - - for(r=0; r!=4; r++) - for(g = 0; g != 4; g++) - for(b = 0; b!=4; b++) - for(v = 0; v!=4; v++){ - den=r; - if(g > den) - den=g; - if(b > den) - den=b; - /* divide check -- pick grey shades */ - if(den==0) - cr=cg=cb=v*17; - else { - num=17*(4*den+v); - cr=r*num/den; - cg=g*num/den; - cb=b*num/den; - } - idx = r*64 + v*16 + ((g*4 + b + v - r) & 15); - _x.map[idx].red = cr*0x0101; - _x.map[idx].green = cg*0x0101; - _x.map[idx].blue = cb*0x0101; - _x.map[idx].pixel = idx; - _x.map[idx].flags = DoRed|DoGreen|DoBlue; - - v7 = v >> 1; - idx7 = r*32 + v7*16 + g*4 + b; - if((v & 1) == v7){ - _x.map7to8[idx7][0] = idx; - if(den == 0) { /* divide check -- pick grey shades */ - cr = ((255.0/7.0)*v7)+0.5; - cg = cr; - cb = cr; - } - else { - num=17*15*(4*den+v7*2)/14; - cr=r*num/den; - cg=g*num/den; - cb=b*num/den; - } - _x.map7[idx7].red = cr*0x0101; - _x.map7[idx7].green = cg*0x0101; - _x.map7[idx7].blue = cb*0x0101; - _x.map7[idx7].pixel = idx7; - _x.map7[idx7].flags = DoRed|DoGreen|DoBlue; - } - else - _x.map7to8[idx7][1] = idx; - } -} - -/* - * Initialize and install the rgbv color map as a private color map - * for this application. It gets the best colors when it has the - * cursor focus. - * - * We always choose the best depth possible, but that might not - * be the default depth. On such "suboptimal" systems, we have to allocate an - * empty color map anyway, according to Axel Belinfante. - */ -static int -setupcmap(XWindow w) -{ - char buf[30]; - int i; - u32int p, pp; - XColor c; - - if(_x.depth <= 1) - return 0; - - if(_x.depth >= 24) { - if(_x.usetable == 0) - _x.cmap = XCreateColormap(_x.display, w, _x.vis, AllocNone); - - /* - * The pixel value returned from XGetPixel needs to - * be converted to RGB so we can call rgb2cmap() - * to translate between 24 bit X and our color. Unfortunately, - * the return value appears to be display server endian - * dependant. Therefore, we run some heuristics to later - * determine how to mask the int value correctly. - * Yeah, I know we can look at _x.vis->byte_order but - * some displays say MSB even though they run on LSB. - * Besides, this is more anal. - */ - c = _x.map[19]; /* known to have different R, G, B values */ - if(!XAllocColor(_x.display, _x.cmap, &c)){ - werrstr("XAllocColor: %r"); - return -1; - } - p = c.pixel; - pp = rgb2cmap((p>>16)&0xff,(p>>8)&0xff,p&0xff); - if(pp != _x.map[19].pixel) { - /* check if endian is other way */ - pp = rgb2cmap(p&0xff,(p>>8)&0xff,(p>>16)&0xff); - if(pp != _x.map[19].pixel){ - werrstr("cannot detect X server byte order"); - return -1; - } - - switch(_x.chan){ - case RGB24: - _x.chan = BGR24; - break; - case XRGB32: - _x.chan = XBGR32; - break; - default: - werrstr("cannot byteswap channel %s", - chantostr(buf, _x.chan)); - break; - } - } - }else if(_x.vis->class == TrueColor || _x.vis->class == DirectColor){ - /* - * Do nothing. We have no way to express a - * mixed-endian 16-bit screen, so pretend they don't exist. - */ - if(_x.usetable == 0) - _x.cmap = XCreateColormap(_x.display, w, _x.vis, AllocNone); - }else if(_x.vis->class == PseudoColor){ - if(_x.usetable == 0){ - _x.cmap = XCreateColormap(_x.display, w, _x.vis, AllocAll); - XStoreColors(_x.display, _x.cmap, _x.map, 256); - for(i = 0; i < 256; i++){ - _x.tox11[i] = i; - _x.toplan9[i] = i; - } - }else{ - for(i = 0; i < 128; i++){ - c = _x.map7[i]; - if(!XAllocColor(_x.display, _x.cmap, &c)){ - werrstr("can't allocate colors in 7-bit map"); - return -1; - } - _x.tox11[_x.map7to8[i][0]] = c.pixel; - _x.tox11[_x.map7to8[i][1]] = c.pixel; - _x.toplan9[c.pixel] = _x.map7to8[i][0]; - } - } - }else{ - werrstr("unsupported visual class %d", _x.vis->class); - return -1; - } - return 0; -} - -void -_flushmemscreen(Rectangle r) -{ - if(_x.nextscreenpm != _x.screenpm){ - qlock(&_x.screenlock); - XSync(_x.display, False); - XFreePixmap(_x.display, _x.screenpm); - _x.screenpm = _x.nextscreenpm; - qunlock(&_x.screenlock); - } - - if(r.min.x >= r.max.x || r.min.y >= r.max.y) - return; - XCopyArea(_x.display, _x.screenpm, _x.drawable, _x.gccopy, r.min.x, r.min.y, - Dx(r), Dy(r), r.min.x, r.min.y); - XFlush(_x.display); -} - -void -_xexpose(XEvent *e) -{ - XExposeEvent *xe; - Rectangle r; - - qlock(&_x.screenlock); - if(_x.screenpm != _x.nextscreenpm){ - qunlock(&_x.screenlock); - return; - } - xe = (XExposeEvent*)e; - r.min.x = xe->x; - r.min.y = xe->y; - r.max.x = xe->x+xe->width; - r.max.y = xe->y+xe->height; - XCopyArea(_x.display, _x.screenpm, _x.drawable, _x.gccopy, r.min.x, r.min.y, - Dx(r), Dy(r), r.min.x, r.min.y); - XSync(_x.display, False); - qunlock(&_x.screenlock); -} - -int -_xdestroy(XEvent *e) -{ - XDestroyWindowEvent *xe; - - xe = (XDestroyWindowEvent*)e; - if(xe->window == _x.drawable){ - _x.destroyed = 1; - return 1; - } - return 0; -} - -int -_xconfigure(XEvent *e) -{ - Rectangle r; - XConfigureEvent *xe = (XConfigureEvent*)e; - - if(!fullscreen){ - int rx, ry; - XWindow w; - if(XTranslateCoordinates(_x.display, _x.drawable, DefaultRootWindow(_x.display), 0, 0, &rx, &ry, &w)) - windowrect = Rect(rx, ry, rx+xe->width, ry+xe->height); - } - - if(xe->width == Dx(_x.screenr) && xe->height == Dy(_x.screenr)) - return 0; - r = Rect(0, 0, xe->width, xe->height); - - qlock(&_x.screenlock); - if(_x.screenpm != _x.nextscreenpm){ - XCopyArea(_x.display, _x.screenpm, _x.drawable, _x.gccopy, r.min.x, r.min.y, - Dx(r), Dy(r), r.min.x, r.min.y); - XSync(_x.display, False); - } - qunlock(&_x.screenlock); - _x.newscreenr = r; - return 1; -} - -int -_xreplacescreenimage(void) -{ - Memimage *m; - XDrawable pixmap; - Rectangle r; - - r = _x.newscreenr; - - pixmap = XCreatePixmap(_x.display, _x.drawable, Dx(r), Dy(r), _x.depth); - m = _xallocmemimage(r, _x.chan, pixmap); - if(_x.nextscreenpm != _x.screenpm) - XFreePixmap(_x.display, _x.nextscreenpm); - _x.nextscreenpm = pixmap; - _x.screenr = r; - _drawreplacescreenimage(m); - return 1; -} diff --git a/src/cmd/devdraw/x11-itrans.c b/src/cmd/devdraw/x11-itrans.c deleted file mode 100644 index 729f2647..00000000 --- a/src/cmd/devdraw/x11-itrans.c +++ /dev/null @@ -1,741 +0,0 @@ -/* input event and data structure translation */ - -#include <u.h> -#include "x11-inc.h" -#ifdef __APPLE__ -#define APPLESNARF -#define Boolean AppleBoolean -#define Rect AppleRect -#define EventMask AppleEventMask -#define Point ApplePoint -#define Cursor AppleCursor -#include <Carbon/Carbon.h> -AUTOFRAMEWORK(Carbon) -#undef Boolean -#undef Rect -#undef EventMask -#undef Point -#undef Cursor -#endif -#include <libc.h> -#include <draw.h> -#include <memdraw.h> -#include <mouse.h> -#include <cursor.h> -#include <keyboard.h> -#include "x11-memdraw.h" -#include "x11-keysym2ucs.h" -#undef time - -static KeySym -__xtoplan9kbd(XEvent *e) -{ - KeySym k; - - if(e->xany.type != KeyPress) - return -1; - needstack(64*1024); /* X has some *huge* buffers in openobject */ - /* and they're even bigger on SuSE */ - XLookupString((XKeyEvent*)e,NULL,0,&k,NULL); - if(k == NoSymbol) - return -1; - - if(k&0xFF00){ - switch(k){ - case XK_BackSpace: - case XK_Tab: - case XK_Escape: - case XK_Delete: - case XK_KP_0: - case XK_KP_1: - case XK_KP_2: - case XK_KP_3: - case XK_KP_4: - case XK_KP_5: - case XK_KP_6: - case XK_KP_7: - case XK_KP_8: - case XK_KP_9: - case XK_KP_Divide: - case XK_KP_Multiply: - case XK_KP_Subtract: - case XK_KP_Add: - case XK_KP_Decimal: - k &= 0x7F; - break; - case XK_Linefeed: - k = '\r'; - break; - case XK_KP_Space: - k = ' '; - break; - case XK_Home: - case XK_KP_Home: - k = Khome; - break; - case XK_Left: - case XK_KP_Left: - k = Kleft; - break; - case XK_Up: - case XK_KP_Up: - k = Kup; - break; - case XK_Down: - case XK_KP_Down: - k = Kdown; - break; - case XK_Right: - case XK_KP_Right: - k = Kright; - break; - case XK_Page_Down: - case XK_KP_Page_Down: - k = Kpgdown; - break; - case XK_End: - case XK_KP_End: - k = Kend; - break; - case XK_Page_Up: - case XK_KP_Page_Up: - k = Kpgup; - break; - case XK_Insert: - case XK_KP_Insert: - k = Kins; - break; - case XK_KP_Enter: - case XK_Return: - k = '\n'; - break; - case XK_Alt_L: - case XK_Meta_L: /* Shift Alt on PCs */ - case XK_Alt_R: - case XK_Meta_R: /* Shift Alt on PCs */ - case XK_Multi_key: - return -1; - default: /* not ISO-1 or tty control */ - if(k>0xff) { - k = _p9keysym2ucs(k); - if(k==-1) return -1; - } - } - } - - /* Compensate for servers that call a minus a hyphen */ - if(k == XK_hyphen) - k = XK_minus; - /* Do control mapping ourselves if translator doesn't */ - if(e->xkey.state&ControlMask) - k &= 0x9f; - if(k == NoSymbol) { - return -1; - } - - return k+0; -} - -int alting; - -void -abortcompose(void) -{ - alting = 0; -} - -static Rune* sendrune(Rune); - -extern int _latin1(Rune*, int); -static Rune* -xtoplan9latin1(XEvent *e) -{ - Rune r; - - r = __xtoplan9kbd(e); - if(r == -1) - return nil; - return sendrune(r); -} - -void -sendalt(void) -{ - sendrune(Kalt); -} - -static Rune* -sendrune(Rune r) -{ - static Rune k[10]; - static int nk; - int n; - - if(alting){ - /* - * Kludge for Mac's X11 3-button emulation. - * It treats Command+Button as button 3, but also - * ends up sending XK_Meta_L twice. - */ - if(r == Kalt){ - alting = 0; - return nil; - } - k[nk++] = r; - n = _latin1(k, nk); - if(n > 0){ - alting = 0; - k[0] = n; - k[1] = 0; - return k; - } - if(n == -1){ - alting = 0; - k[nk] = 0; - return k; - } - /* n < -1, need more input */ - return nil; - }else if(r == Kalt){ - alting = 1; - nk = 0; - return nil; - }else{ - k[0] = r; - k[1] = 0; - return k; - } -} - -int -_xtoplan9kbd(XEvent *e) -{ - static Rune *r; - - if(e == (XEvent*)-1){ - assert(r); - r--; - return 0; - } - if(e) - r = xtoplan9latin1(e); - if(r && *r) - return *r++; - return -1; -} - -int -_xtoplan9mouse(XEvent *e, Mouse *m) -{ - int s; - XButtonEvent *be; - XMotionEvent *me; - - if(_x.putsnarf != _x.assertsnarf){ - _x.assertsnarf = _x.putsnarf; - XSetSelectionOwner(_x.display, XA_PRIMARY, _x.drawable, CurrentTime); - if(_x.clipboard != None) - XSetSelectionOwner(_x.display, _x.clipboard, _x.drawable, CurrentTime); - XFlush(_x.display); - } - - switch(e->type){ - case ButtonPress: - be = (XButtonEvent*)e; - - /* - * Fake message, just sent to make us announce snarf. - * Apparently state and button are 16 and 8 bits on - * the wire, since they are truncated by the time they - * get to us. - */ - if(be->send_event - && (~be->state&0xFFFF)==0 - && (~be->button&0xFF)==0) - return -1; - /* BUG? on mac need to inherit these from elsewhere? */ - m->xy.x = be->x; - m->xy.y = be->y; - s = be->state; - m->msec = be->time; - switch(be->button){ - case 1: - s |= Button1Mask; - break; - case 2: - s |= Button2Mask; - break; - case 3: - s |= Button3Mask; - break; - case 4: - s |= Button4Mask; - break; - case 5: - s |= Button5Mask; - break; - } - break; - case ButtonRelease: - be = (XButtonEvent*)e; - m->xy.x = be->x; - m->xy.y = be->y; - s = be->state; - m->msec = be->time; - switch(be->button){ - case 1: - s &= ~Button1Mask; - break; - case 2: - s &= ~Button2Mask; - break; - case 3: - s &= ~Button3Mask; - break; - case 4: - s &= ~Button4Mask; - break; - case 5: - s &= ~Button5Mask; - break; - } - break; - - case MotionNotify: - me = (XMotionEvent*)e; - s = me->state; - m->xy.x = me->x; - m->xy.y = me->y; - m->msec = me->time; - return 0; // do not set buttons - - default: - return -1; - } - - m->buttons = 0; - if(s & Button1Mask) - m->buttons |= 1; - if(s & Button2Mask) - m->buttons |= 2; - if(s & Button3Mask) - m->buttons |= 4; - if(s & Button4Mask) - m->buttons |= 8; - if(s & Button5Mask) - m->buttons |= 16; - return 0; -} - -void -_xmoveto(Point p) -{ - XWarpPointer(_x.display, None, _x.drawable, 0, 0, 0, 0, p.x, p.y); - XFlush(_x.display); -} - -static int -revbyte(int b) -{ - int r; - - r = 0; - r |= (b&0x01) << 7; - r |= (b&0x02) << 5; - r |= (b&0x04) << 3; - r |= (b&0x08) << 1; - r |= (b&0x10) >> 1; - r |= (b&0x20) >> 3; - r |= (b&0x40) >> 5; - r |= (b&0x80) >> 7; - return r; -} - -static void -xcursorarrow(void) -{ - if(_x.cursor != 0){ - XFreeCursor(_x.display, _x.cursor); - _x.cursor = 0; - } - XUndefineCursor(_x.display, _x.drawable); - XFlush(_x.display); -} - - -void -_xsetcursor(Cursor *c) -{ - XColor fg, bg; - XCursor xc; - Pixmap xsrc, xmask; - int i; - uchar src[2*16], mask[2*16]; - - if(c == nil){ - xcursorarrow(); - return; - } - for(i=0; i<2*16; i++){ - src[i] = revbyte(c->set[i]); - mask[i] = revbyte(c->set[i] | c->clr[i]); - } - - fg = _x.map[0]; - bg = _x.map[255]; - xsrc = XCreateBitmapFromData(_x.display, _x.drawable, (char*)src, 16, 16); - xmask = XCreateBitmapFromData(_x.display, _x.drawable, (char*)mask, 16, 16); - xc = XCreatePixmapCursor(_x.display, xsrc, xmask, &fg, &bg, -c->offset.x, -c->offset.y); - if(xc != 0) { - XDefineCursor(_x.display, _x.drawable, xc); - if(_x.cursor != 0) - XFreeCursor(_x.display, _x.cursor); - _x.cursor = xc; - } - XFreePixmap(_x.display, xsrc); - XFreePixmap(_x.display, xmask); - XFlush(_x.display); -} - -struct { - QLock lk; - char buf[SnarfSize]; -#ifdef APPLESNARF - Rune rbuf[SnarfSize]; - PasteboardRef apple; -#endif -} clip; - -static uchar* -_xgetsnarffrom(XWindow w, Atom clipboard, Atom target, int timeout0, int timeout) -{ - Atom prop, type; - ulong len, lastlen, dummy; - int fmt, i; - uchar *data, *xdata; - - /* - * We should be waiting for SelectionNotify here, but it might never - * come, and we have no way to time out. Instead, we will clear - * local property #1, request our buddy to fill it in for us, and poll - * until he's done or we get tired of waiting. - */ - prop = 1; - XChangeProperty(_x.display, _x.drawable, prop, target, 8, PropModeReplace, (uchar*)"", 0); - XConvertSelection(_x.display, clipboard, target, prop, _x.drawable, CurrentTime); - XFlush(_x.display); - lastlen = 0; - timeout0 = (timeout0 + 9)/10; - timeout = (timeout + 9)/10; - for(i=0; i<timeout0 || (lastlen!=0 && i<timeout); i++){ - usleep(10*1000); - XGetWindowProperty(_x.display, _x.drawable, prop, 0, 0, 0, AnyPropertyType, - &type, &fmt, &dummy, &len, &xdata); - if(lastlen == len && len > 0){ - XFree(xdata); - break; - } - lastlen = len; - XFree(xdata); - } - if(len == 0) - return nil; - - /* get the property */ - xdata = nil; - XGetWindowProperty(_x.display, _x.drawable, prop, 0, SnarfSize/sizeof(ulong), 0, - AnyPropertyType, &type, &fmt, &len, &dummy, &xdata); - if((type != target && type != XA_STRING && type != _x.utf8string) || len == 0){ - if(xdata) - XFree(xdata); - return nil; - } - if(xdata){ - data = (uchar*)strdup((char*)xdata); - XFree(xdata); - return data; - } - return nil; -} - -char* -_xgetsnarf(void) -{ - uchar *data; - Atom clipboard; - XWindow w; - - qlock(&clip.lk); - /* - * Have we snarfed recently and the X server hasn't caught up? - */ - if(_x.putsnarf != _x.assertsnarf) - goto mine; - - /* - * Is there a primary selection (highlighted text in an xterm)? - */ - clipboard = XA_PRIMARY; - w = XGetSelectionOwner(_x.display, XA_PRIMARY); - if(w == _x.drawable){ - mine: - data = (uchar*)strdup(clip.buf); - goto out; - } - - /* - * If not, is there a clipboard selection? - */ - if(w == None && _x.clipboard != None){ - clipboard = _x.clipboard; - w = XGetSelectionOwner(_x.display, _x.clipboard); - if(w == _x.drawable) - goto mine; - } - - /* - * If not, give up. - */ - if(w == None){ - data = nil; - goto out; - } - - if((data = _xgetsnarffrom(w, clipboard, _x.utf8string, 10, 100)) == nil) - if((data = _xgetsnarffrom(w, clipboard, XA_STRING, 10, 100)) == nil){ - /* nothing left to do */ - } - -out: - qunlock(&clip.lk); - return (char*)data; -} - -void -__xputsnarf(char *data) -{ - XButtonEvent e; - - if(strlen(data) >= SnarfSize) - return; - qlock(&clip.lk); - strcpy(clip.buf, data); - /* leave note for mouse proc to assert selection ownership */ - _x.putsnarf++; - - /* send mouse a fake event so snarf is announced */ - memset(&e, 0, sizeof e); - e.type = ButtonPress; - e.window = _x.drawable; - e.state = ~0; - e.button = ~0; - XSendEvent(_x.display, _x.drawable, True, ButtonPressMask, (XEvent*)&e); - XFlush(_x.display); - qunlock(&clip.lk); -} - -int -_xselect(XEvent *e) -{ - char *name; - XEvent r; - XSelectionRequestEvent *xe; - Atom a[4]; - - memset(&r, 0, sizeof r); - xe = (XSelectionRequestEvent*)e; -if(0) fprint(2, "xselect target=%d requestor=%d property=%d selection=%d (sizeof atom=%d)\n", - xe->target, xe->requestor, xe->property, xe->selection, sizeof a[0]); - r.xselection.property = xe->property; - if(xe->target == _x.targets){ - a[0] = _x.utf8string; - a[1] = XA_STRING; - a[2] = _x.text; - a[3] = _x.compoundtext; - XChangeProperty(_x.display, xe->requestor, xe->property, XA_ATOM, - 32, PropModeReplace, (uchar*)a, nelem(a)); - }else if(xe->target == XA_STRING - || xe->target == _x.utf8string - || xe->target == _x.text - || xe->target == _x.compoundtext - || ((name = XGetAtomName(_x.display, xe->target)) && strcmp(name, "text/plain;charset=UTF-8") == 0)){ - /* text/plain;charset=UTF-8 seems nonstandard but is used by Synergy */ - /* if the target is STRING we're supposed to reply with Latin1 XXX */ - qlock(&clip.lk); - XChangeProperty(_x.display, xe->requestor, xe->property, xe->target, - 8, PropModeReplace, (uchar*)clip.buf, strlen(clip.buf)); - qunlock(&clip.lk); - }else{ - if(strcmp(name, "TIMESTAMP") != 0) - fprint(2, "%s: cannot handle selection request for '%s' (%d)\n", argv0, name, (int)xe->target); - r.xselection.property = None; - } - - r.xselection.display = xe->display; - /* r.xselection.property filled above */ - r.xselection.target = xe->target; - r.xselection.type = SelectionNotify; - r.xselection.requestor = xe->requestor; - r.xselection.time = xe->time; - r.xselection.send_event = True; - r.xselection.selection = xe->selection; - XSendEvent(_x.display, xe->requestor, False, 0, &r); - XFlush(_x.display); - return 0; -} - -#ifdef APPLESNARF -char* -_applegetsnarf(void) -{ - char *s, *t; - CFArrayRef flavors; - CFDataRef data; - CFIndex nflavor, ndata, j; - CFStringRef type; - ItemCount nitem; - PasteboardItemID id; - PasteboardSyncFlags flags; - UInt32 i; - -/* fprint(2, "applegetsnarf\n"); */ - qlock(&clip.lk); - if(clip.apple == nil){ - if(PasteboardCreate(kPasteboardClipboard, &clip.apple) != noErr){ - fprint(2, "apple pasteboard create failed\n"); - qunlock(&clip.lk); - return nil; - } - } - flags = PasteboardSynchronize(clip.apple); - if(flags&kPasteboardClientIsOwner){ - s = strdup(clip.buf); - qunlock(&clip.lk); - return s; - } - if(PasteboardGetItemCount(clip.apple, &nitem) != noErr){ - fprint(2, "apple pasteboard get item count failed\n"); - qunlock(&clip.lk); - return nil; - } - for(i=1; i<=nitem; i++){ - if(PasteboardGetItemIdentifier(clip.apple, i, &id) != noErr) - continue; - if(PasteboardCopyItemFlavors(clip.apple, id, &flavors) != noErr) - continue; - nflavor = CFArrayGetCount(flavors); - for(j=0; j<nflavor; j++){ - type = (CFStringRef)CFArrayGetValueAtIndex(flavors, j); - if(!UTTypeConformsTo(type, CFSTR("public.utf16-plain-text"))) - continue; - if(PasteboardCopyItemFlavorData(clip.apple, id, type, &data) != noErr) - continue; - ndata = CFDataGetLength(data); - qunlock(&clip.lk); - s = smprint("%.*S", ndata/2, (Rune*)CFDataGetBytePtr(data)); - CFRelease(flavors); - CFRelease(data); - for(t=s; *t; t++) - if(*t == '\r') - *t = '\n'; - return s; - } - CFRelease(flavors); - } - qunlock(&clip.lk); - return nil; -} - -void -_appleputsnarf(char *s) -{ - CFDataRef cfdata; - PasteboardSyncFlags flags; - -/* fprint(2, "appleputsnarf\n"); */ - - if(strlen(s) >= SnarfSize) - return; - qlock(&clip.lk); - strcpy(clip.buf, s); - runesnprint(clip.rbuf, nelem(clip.rbuf), "%s", s); - if(clip.apple == nil){ - if(PasteboardCreate(kPasteboardClipboard, &clip.apple) != noErr){ - fprint(2, "apple pasteboard create failed\n"); - qunlock(&clip.lk); - return; - } - } - if(PasteboardClear(clip.apple) != noErr){ - fprint(2, "apple pasteboard clear failed\n"); - qunlock(&clip.lk); - return; - } - flags = PasteboardSynchronize(clip.apple); - if((flags&kPasteboardModified) || !(flags&kPasteboardClientIsOwner)){ - fprint(2, "apple pasteboard cannot assert ownership\n"); - qunlock(&clip.lk); - return; - } - cfdata = CFDataCreate(kCFAllocatorDefault, - (uchar*)clip.rbuf, runestrlen(clip.rbuf)*2); - if(cfdata == nil){ - fprint(2, "apple pasteboard cfdatacreate failed\n"); - qunlock(&clip.lk); - return; - } - if(PasteboardPutItemFlavor(clip.apple, (PasteboardItemID)1, - CFSTR("public.utf16-plain-text"), cfdata, 0) != noErr){ - fprint(2, "apple pasteboard putitem failed\n"); - CFRelease(cfdata); - qunlock(&clip.lk); - return; - } - /* CFRelease(cfdata); ??? */ - qunlock(&clip.lk); -} -#endif /* APPLESNARF */ - -void -_xputsnarf(char *data) -{ -#ifdef APPLESNARF - _appleputsnarf(data); -#endif - __xputsnarf(data); -} - -/* - * Send the mouse event back to the window manager. - * So that 9term can tell rio to pop up its button3 menu. - */ -void -_xbouncemouse(Mouse *m) -{ - XButtonEvent e; - XWindow dw; - - e.type = ButtonPress; - e.state = 0; - e.button = 0; - if(m->buttons&1) - e.button = 1; - else if(m->buttons&2) - e.button = 2; - else if(m->buttons&4) - e.button = 3; - e.same_screen = 1; - XTranslateCoordinates(_x.display, _x.drawable, - DefaultRootWindow(_x.display), - m->xy.x, m->xy.y, &e.x_root, &e.y_root, &dw); - e.root = DefaultRootWindow(_x.display); - e.window = e.root; - e.subwindow = None; - e.x = e.x_root; - e.y = e.y_root; -#undef time - e.time = CurrentTime; - XUngrabPointer(_x.display, m->msec); - XSendEvent(_x.display, e.root, True, ButtonPressMask, (XEvent*)&e); - XFlush(_x.display); -} diff --git a/src/cmd/devdraw/x11-keysym2ucs.c b/src/cmd/devdraw/x11-keysym2ucs.c index 108f153c..853fa552 100644 --- a/src/cmd/devdraw/x11-keysym2ucs.c +++ b/src/cmd/devdraw/x11-keysym2ucs.c @@ -34,7 +34,7 @@ */ #ifndef KEYSYM2UCS_INCLUDED - + #include "x11-keysym2ucs.h" #define VISIBLE /* */ diff --git a/src/cmd/devdraw/x11-load.c b/src/cmd/devdraw/x11-load.c index a7446f37..895e1109 100644 --- a/src/cmd/devdraw/x11-load.c +++ b/src/cmd/devdraw/x11-load.c @@ -15,4 +15,3 @@ loadmemimage(Memimage *i, Rectangle r, uchar *data, int ndata) _xputxdata(i, r); return n; } - diff --git a/src/cmd/devdraw/x11-memdraw.h b/src/cmd/devdraw/x11-memdraw.h index d2cab13b..0c78b9b6 100644 --- a/src/cmd/devdraw/x11-memdraw.h +++ b/src/cmd/devdraw/x11-memdraw.h @@ -4,6 +4,7 @@ typedef struct Xmem Xmem; typedef struct Xprivate Xprivate; +typedef struct Xwin Xwin; enum { @@ -26,7 +27,6 @@ struct Xprivate { XDisplay *display; int fd; /* of display */ int depth; /* of screen */ - XDrawable drawable; XColor map[256]; XColor map7[128]; uchar map7to8[128][2]; @@ -50,12 +50,6 @@ struct Xprivate { u32int gczeropixmap; XGC gczero0; u32int gczero0pixmap; - Rectangle newscreenr; - Memimage* screenimage; - QLock screenlock; - XDrawable screenpm; - XDrawable nextscreenpm; - Rectangle screenr; int toplan9[256]; int tox11[256]; int usetable; @@ -70,9 +64,35 @@ struct Xprivate { Atom wmprotos; uint putsnarf; uint assertsnarf; + int kbuttons; + int kstate; + int altdown; + + Xwin* windows; +}; + +struct Client; + +struct Xwin +{ + XDrawable drawable; + struct Client* client; + + Rectangle newscreenr; + Memimage* screenimage; + XDrawable screenpm; + XDrawable nextscreenpm; + Rectangle screenr; + Rectangle screenrect; + Rectangle windowrect; + int fullscreen; int destroyed; + + Xwin* next; }; +void xlock(void); +void xunlock(void); extern Xprivate _x; extern Memimage *_xallocmemimage(Rectangle, u32int, int); @@ -83,35 +103,3 @@ extern void _xfreexdata(Memimage*); extern XImage *_xgetxdata(Memimage*, Rectangle); extern void _xputxdata(Memimage*, Rectangle); -struct Mouse; -extern int _xtoplan9mouse(XEvent*, struct Mouse*); -extern int _xtoplan9kbd(XEvent*); -extern void _xexpose(XEvent*); -extern int _xselect(XEvent*); -extern int _xconfigure(XEvent*); -extern int _xdestroy(XEvent*); -extern void _flushmemscreen(Rectangle); -extern void _xmoveto(Point); -struct Cursor; -extern void _xsetcursor(struct Cursor*); -extern void _xbouncemouse(Mouse*); -extern int _xsetlabel(char*); -extern Memimage* _xattach(char*, char*); -extern char* _xgetsnarf(void); -extern void _xputsnarf(char *data); -extern void _xtopwindow(void); -extern void _xresizewindow(Rectangle); -extern void _xmovewindow(Rectangle); -extern int _xreplacescreenimage(void); - -#define MouseMask (\ - ButtonPressMask|\ - ButtonReleaseMask|\ - PointerMotionMask|\ - Button1MotionMask|\ - Button2MotionMask|\ - Button3MotionMask) - -extern Rectangle screenrect; -extern Rectangle windowrect; -extern int fullscreen; diff --git a/src/cmd/devdraw/x11-pixelbits.c b/src/cmd/devdraw/x11-pixelbits.c index 5cbdded8..5227f39b 100644 --- a/src/cmd/devdraw/x11-pixelbits.c +++ b/src/cmd/devdraw/x11-pixelbits.c @@ -12,5 +12,3 @@ pixelbits(Memimage *m, Point p) _xgetxdata(m, Rect(p.x, p.y, p.x+1, p.y+1)); return _pixelbits(m, p); } - - diff --git a/src/cmd/devdraw/x11-screen.c b/src/cmd/devdraw/x11-screen.c new file mode 100644 index 00000000..8026e1e6 --- /dev/null +++ b/src/cmd/devdraw/x11-screen.c @@ -0,0 +1,1759 @@ +#include <u.h> +#include "x11-inc.h" +#include "x11-keysym2ucs.h" +#include <errno.h> +#include <libc.h> +#include <draw.h> +#include <memdraw.h> +#include <memlayer.h> +#include <keyboard.h> +#include <mouse.h> +#include <cursor.h> +#include <thread.h> +#include "x11-memdraw.h" +#include "devdraw.h" + +#undef time + +static void plan9cmap(void); +static int setupcmap(XWindow); +static XGC xgc(XDrawable, int, int); +#define Mask MouseMask|ExposureMask|StructureNotifyMask|KeyPressMask|KeyReleaseMask|EnterWindowMask|LeaveWindowMask|FocusChangeMask + +#define MouseMask (\ + ButtonPressMask|\ + ButtonReleaseMask|\ + PointerMotionMask|\ + Button1MotionMask|\ + Button2MotionMask|\ + Button3MotionMask) + +Xprivate _x; + +static void runxevent(XEvent *xev); +static int _xconfigure(Xwin *w, XEvent *e); +static int _xdestroy(Xwin *w, XEvent *e); +static void _xexpose(Xwin *w, XEvent *e); +static int _xreplacescreenimage(Client *client); +static int _xtoplan9mouse(Xwin *w, XEvent *e, Mouse *m); +static void _xmovewindow(Xwin *w, Rectangle r); +static int _xtoplan9kbd(XEvent *e); +static int _xselect(XEvent *e); + +static Xwin* +newxwin(Client *c) +{ + Xwin *w; + + w = mallocz(sizeof *w, 1); + if(w == nil) + sysfatal("out of memory"); + w->client = c; + w->next = _x.windows; + _x.windows = w; + c->view = w; + return w; +} + +static Xwin* +findxwin(XDrawable d) +{ + Xwin *w, **l; + + for(l=&_x.windows; (w=*l) != nil; l=&w->next) { + if(w->drawable == d) { + /* move to front */ + *l = w->next; + w->next = _x.windows; + _x.windows = w; + return w; + } + } + return nil; +} + +static int +xerror(XDisplay *d, XErrorEvent *e) +{ + char buf[200]; + + if(e->request_code == 42) /* XSetInputFocus */ + return 0; + if(e->request_code == 18) /* XChangeProperty */ + return 0; + /* + * BadDrawable happens in apps that get resized a LOT, + * e.g. when KDE is configured to resize continuously + * during a window drag. + */ + if(e->error_code == 9) /* BadDrawable */ + return 0; + + fprint(2, "X error: error_code=%d, request_code=%d, minor=%d disp=%p\n", + e->error_code, e->request_code, e->minor_code, d); + XGetErrorText(d, e->error_code, buf, sizeof buf); + fprint(2, "%s\n", buf); + return 0; +} + +static int +xioerror(XDisplay *d) +{ + /*print("X I/O error\n"); */ + exit(0); + /*sysfatal("X I/O error\n");*/ + abort(); + return -1; +} + +static void xloop(void); + +static QLock xlk; + +void +xlock(void) +{ + qlock(&xlk); +} + +void +xunlock(void) +{ + qunlock(&xlk); +} + +void +gfx_main(void) +{ + char *disp; + int i, n, xrootid; + XPixmapFormatValues *pfmt; + XScreen *xscreen; + XVisualInfo xvi; + XWindow xrootwin; + + /* + if(XInitThreads() == 0) + sysfatal("XInitThread: %r"); + */ + + /* + * Connect to X server. + */ + _x.display = XOpenDisplay(NULL); + if(_x.display == nil){ + disp = getenv("DISPLAY"); + werrstr("XOpenDisplay %s: %r", disp ? disp : ":0"); + free(disp); + sysfatal("%r"); + } + _x.fd = ConnectionNumber(_x.display); + XSetErrorHandler(xerror); + XSetIOErrorHandler(xioerror); + xrootid = DefaultScreen(_x.display); + xrootwin = DefaultRootWindow(_x.display); + + /* + * Figure out underlying screen format. + */ + if(XMatchVisualInfo(_x.display, xrootid, 24, TrueColor, &xvi) + || XMatchVisualInfo(_x.display, xrootid, 24, DirectColor, &xvi)){ + _x.vis = xvi.visual; + _x.depth = 24; + } + else + if(XMatchVisualInfo(_x.display, xrootid, 16, TrueColor, &xvi) + || XMatchVisualInfo(_x.display, xrootid, 16, DirectColor, &xvi)){ + _x.vis = xvi.visual; + _x.depth = 16; + } + else + if(XMatchVisualInfo(_x.display, xrootid, 15, TrueColor, &xvi) + || XMatchVisualInfo(_x.display, xrootid, 15, DirectColor, &xvi)){ + _x.vis = xvi.visual; + _x.depth = 15; + } + else + if(XMatchVisualInfo(_x.display, xrootid, 8, PseudoColor, &xvi) + || XMatchVisualInfo(_x.display, xrootid, 8, StaticColor, &xvi)){ + if(_x.depth > 8){ + werrstr("can't deal with colormapped depth %d screens", + _x.depth); + goto err0; + } + _x.vis = xvi.visual; + _x.depth = 8; + } + else{ + _x.depth = DefaultDepth(_x.display, xrootid); + if(_x.depth != 8){ + werrstr("can't understand depth %d screen", _x.depth); + goto err0; + } + _x.vis = DefaultVisual(_x.display, xrootid); + } + + if(DefaultDepth(_x.display, xrootid) == _x.depth) + _x.usetable = 1; + + /* + * _x.depth is only the number of significant pixel bits, + * not the total number of pixel bits. We need to walk the + * display list to find how many actual bits are used + * per pixel. + */ + _x.chan = 0; + pfmt = XListPixmapFormats(_x.display, &n); + for(i=0; i<n; i++){ + if(pfmt[i].depth == _x.depth){ + switch(pfmt[i].bits_per_pixel){ + case 1: /* untested */ + _x.chan = GREY1; + break; + case 2: /* untested */ + _x.chan = GREY2; + break; + case 4: /* untested */ + _x.chan = GREY4; + break; + case 8: + _x.chan = CMAP8; + break; + case 15: + _x.chan = RGB15; + break; + case 16: /* how to tell RGB15? */ + _x.chan = RGB16; + break; + case 24: /* untested (impossible?) */ + _x.chan = RGB24; + break; + case 32: + _x.chan = XRGB32; + break; + } + } + } + XFree(pfmt); + if(_x.chan == 0){ + werrstr("could not determine screen pixel format"); + goto err0; + } + + /* + * Set up color map if necessary. + */ + xscreen = DefaultScreenOfDisplay(_x.display); + _x.cmap = DefaultColormapOfScreen(xscreen); + if(_x.vis->class != StaticColor){ + plan9cmap(); + setupcmap(xrootwin); + } + gfx_started(); + xloop(); + +err0: + XCloseDisplay(_x.display); + sysfatal("%r"); +} + +static void +xloop(void) +{ + fd_set rd, wr, xx; + XEvent event; + + xlock(); + _x.fd = ConnectionNumber(_x.display); + for(;;) { + FD_ZERO(&rd); + FD_ZERO(&wr); + FD_ZERO(&xx); + FD_SET(_x.fd, &rd); + FD_SET(_x.fd, &xx); + if(_x.windows != nil) + XSelectInput(_x.display, _x.windows->drawable, Mask); // TODO: when is this needed? + XFlush(_x.display); + xunlock(); + + again: + if(select(_x.fd+1, &rd, &wr, &xx, nil) < 0) { + if(errno == EINTR) + goto again; + sysfatal("select: %r"); // TODO: quiet exit? + } + + xlock(); + while(XPending(_x.display)) { + XNextEvent(_x.display, &event); + runxevent(&event); + } + } +} + +/* + * Handle an incoming X event. + */ +static void +runxevent(XEvent *xev) +{ + int c; + KeySym k; + static Mouse m; + XButtonEvent *be; + XKeyEvent *ke; + Xwin *w; + +#ifdef SHOWEVENT + static int first = 1; + if(first){ + dup(create("/tmp/devdraw.out", OWRITE, 0666), 1); + setbuf(stdout, 0); + first = 0; + } +#endif + + if(xev == 0) + return; + +#ifdef SHOWEVENT + print("\n"); + ShowEvent(xev); +#endif + + w = nil; + switch(xev->type){ + case Expose: + w = findxwin(((XExposeEvent*)xev)->window); + break; + case DestroyNotify: + w = findxwin(((XDestroyWindowEvent*)xev)->window); + break; + case ConfigureNotify: + w = findxwin(((XConfigureEvent*)xev)->window); + break; + case ButtonPress: + case ButtonRelease: + w = findxwin(((XButtonEvent*)xev)->window); + break; + case MotionNotify: + w = findxwin(((XMotionEvent*)xev)->window); + break; + case KeyRelease: + case KeyPress: + w = findxwin(((XKeyEvent*)xev)->window); + break; + case FocusOut: + w = findxwin(((XFocusChangeEvent*)xev)->window); + break; + } + if(w == nil) + w = _x.windows; + + switch(xev->type){ + case Expose: + _xexpose(w, xev); + break; + + case DestroyNotify: + if(_xdestroy(w, xev)) + threadexitsall(nil); + break; + + case ConfigureNotify: + if(_xconfigure(w, xev)) + _xreplacescreenimage(w->client); + break; + + case ButtonPress: + be = (XButtonEvent*)xev; + if(be->button == 1) { + if(_x.kstate & ControlMask) + be->button = 2; + else if(_x.kstate & Mod1Mask) + be->button = 3; + } + // fall through + case ButtonRelease: + _x.altdown = 0; + // fall through + case MotionNotify: + if(_xtoplan9mouse(w, xev, &m) < 0) + return; + gfx_mousetrack(w->client, m.xy.x, m.xy.y, m.buttons|_x.kbuttons, m.msec); + break; + + case KeyRelease: + case KeyPress: + ke = (XKeyEvent*)xev; + XLookupString(ke, NULL, 0, &k, NULL); + c = ke->state; + switch(k) { + case XK_Alt_L: + case XK_Meta_L: /* Shift Alt on PCs */ + case XK_Alt_R: + case XK_Meta_R: /* Shift Alt on PCs */ + case XK_Multi_key: + if(xev->type == KeyPress) + _x.altdown = 1; + else if(_x.altdown) { + _x.altdown = 0; + gfx_keystroke(w->client, Kalt); + } + break; + } + + switch(k) { + case XK_Control_L: + if(xev->type == KeyPress) + c |= ControlMask; + else + c &= ~ControlMask; + goto kbutton; + case XK_Alt_L: + case XK_Shift_L: + if(xev->type == KeyPress) + c |= Mod1Mask; + else + c &= ~Mod1Mask; + kbutton: + _x.kstate = c; + if(m.buttons || _x.kbuttons) { + _x.altdown = 0; // used alt + _x.kbuttons = 0; + if(c & ControlMask) + _x.kbuttons |= 2; + if(c & Mod1Mask) + _x.kbuttons |= 4; + gfx_mousetrack(w->client, m.xy.x, m.xy.y, m.buttons|_x.kbuttons, m.msec); + break; + } + } + + if(xev->type != KeyPress) + break; + if(k == XK_F11){ + w->fullscreen = !w->fullscreen; + _xmovewindow(w, w->fullscreen ? w->screenrect : w->windowrect); + return; + } + if((c = _xtoplan9kbd(xev)) < 0) + return; + gfx_keystroke(w->client, c); + break; + + case FocusOut: + /* + * Some key combinations (e.g. Alt-Tab) can cause us + * to see the key down event without the key up event, + * so clear out the keyboard state when we lose the focus. + */ + _x.kstate = 0; + _x.altdown = 0; + gfx_abortcompose(w->client); + break; + + case SelectionRequest: + _xselect(xev); + break; + } +} + + +static Memimage* +xattach(Client *client, char *label, char *winsize) +{ + char *argv[2]; + int havemin, height, mask, width, x, y; + Rectangle r; + XClassHint classhint; + XDrawable pmid; + XScreen *xscreen; + XSetWindowAttributes attr; + XSizeHints normalhint; + XTextProperty name; + XWindow xrootwin; + XWindowAttributes wattr; + XWMHints hint; + Atom atoms[2]; + Xwin *w; + + USED(client); + xscreen = DefaultScreenOfDisplay(_x.display); + xrootwin = DefaultRootWindow(_x.display); + + /* + * We get to choose the initial rectangle size. + * This is arbitrary. In theory we should read the + * command line and allow the traditional X options. + */ + mask = 0; + x = 0; + y = 0; + if(winsize && winsize[0]){ + if(parsewinsize(winsize, &r, &havemin) < 0) + sysfatal("%r"); + }else{ + /* + * Parse the various X resources. Thanks to Peter Canning. + */ + char *screen_resources, *display_resources, *geom, + *geomrestype, *home, *file, *dpitype; + XrmDatabase database; + XrmValue geomres, dpires; + + database = XrmGetDatabase(_x.display); + screen_resources = XScreenResourceString(xscreen); + if(screen_resources != nil){ + XrmCombineDatabase(XrmGetStringDatabase(screen_resources), &database, False); + XFree(screen_resources); + } + + display_resources = XResourceManagerString(_x.display); + if(display_resources == nil){ + home = getenv("HOME"); + if(home!=nil && (file=smprint("%s/.Xdefaults", home)) != nil){ + XrmCombineFileDatabase(file, &database, False); + free(file); + } + free(home); + }else + XrmCombineDatabase(XrmGetStringDatabase(display_resources), &database, False); + + if (XrmGetResource(database, "Xft.dpi", "String", &dpitype, &dpires) == True) { + if (dpires.addr) { + client->displaydpi = atoi(dpires.addr); + } + } + geom = smprint("%s.geometry", label); + if(geom && XrmGetResource(database, geom, nil, &geomrestype, &geomres)) + mask = XParseGeometry(geomres.addr, &x, &y, (uint*)&width, (uint*)&height); + XrmDestroyDatabase(database); + free(geom); + + if((mask & WidthValue) && (mask & HeightValue)){ + r = Rect(0, 0, width, height); + }else{ + r = Rect(0, 0, WidthOfScreen(xscreen)*3/4, + HeightOfScreen(xscreen)*3/4); + if(Dx(r) > Dy(r)*3/2) + r.max.x = r.min.x + Dy(r)*3/2; + if(Dy(r) > Dx(r)*3/2) + r.max.y = r.min.y + Dx(r)*3/2; + } + if(mask & XNegative){ + x += WidthOfScreen(xscreen); + } + if(mask & YNegative){ + y += HeightOfScreen(xscreen); + } + havemin = 0; + } + w = newxwin(client); + w->screenrect = Rect(0, 0, WidthOfScreen(xscreen), HeightOfScreen(xscreen)); + w->windowrect = r; + + memset(&attr, 0, sizeof attr); + attr.colormap = _x.cmap; + attr.background_pixel = ~0; + attr.border_pixel = 0; + w->drawable = XCreateWindow( + _x.display, /* display */ + xrootwin, /* parent */ + x, /* x */ + y, /* y */ + Dx(r), /* width */ + Dy(r), /* height */ + 0, /* border width */ + _x.depth, /* depth */ + InputOutput, /* class */ + _x.vis, /* visual */ + /* valuemask */ + CWBackPixel|CWBorderPixel|CWColormap, + &attr /* attributes (the above aren't?!) */ + ); + + /* + * Label and other properties required by ICCCCM. + */ + memset(&name, 0, sizeof name); + if(label == nil) + label = "pjw-face-here"; + name.value = (uchar*)label; + name.encoding = XA_STRING; + name.format = 8; + name.nitems = strlen((char*)name.value); + + memset(&normalhint, 0, sizeof normalhint); + normalhint.flags = PSize|PMaxSize; + if(winsize && winsize[0]){ + normalhint.flags &= ~PSize; + normalhint.flags |= USSize; + normalhint.width = Dx(r); + normalhint.height = Dy(r); + }else{ + if((mask & WidthValue) && (mask & HeightValue)){ + normalhint.flags &= ~PSize; + normalhint.flags |= USSize; + normalhint.width = width; + normalhint.height = height; + } + if((mask & WidthValue) && (mask & HeightValue)){ + normalhint.flags |= USPosition; + normalhint.x = x; + normalhint.y = y; + } + } + + normalhint.max_width = WidthOfScreen(xscreen); + normalhint.max_height = HeightOfScreen(xscreen); + + memset(&hint, 0, sizeof hint); + hint.flags = InputHint|StateHint; + hint.input = 1; + hint.initial_state = NormalState; + + memset(&classhint, 0, sizeof classhint); + classhint.res_name = label; + classhint.res_class = label; + + argv[0] = label; + argv[1] = nil; + + XSetWMProperties( + _x.display, /* display */ + w->drawable, /* window */ + &name, /* XA_WM_NAME property */ + &name, /* XA_WM_ICON_NAME property */ + argv, /* XA_WM_COMMAND */ + 1, /* argc */ + &normalhint, /* XA_WM_NORMAL_HINTS */ + &hint, /* XA_WM_HINTS */ + &classhint /* XA_WM_CLASSHINTS */ + ); + XFlush(_x.display); + + if(havemin){ + XWindowChanges ch; + + memset(&ch, 0, sizeof ch); + ch.x = r.min.x; + ch.y = r.min.y; + XConfigureWindow(_x.display, w->drawable, CWX|CWY, &ch); + /* + * Must pretend origin is 0,0 for X. + */ + r = Rect(0,0,Dx(r),Dy(r)); + } + /* + * Look up clipboard atom. + */ + if(_x.clipboard == 0) { + _x.clipboard = XInternAtom(_x.display, "CLIPBOARD", False); + _x.utf8string = XInternAtom(_x.display, "UTF8_STRING", False); + _x.targets = XInternAtom(_x.display, "TARGETS", False); + _x.text = XInternAtom(_x.display, "TEXT", False); + _x.compoundtext = XInternAtom(_x.display, "COMPOUND_TEXT", False); + _x.takefocus = XInternAtom(_x.display, "WM_TAKE_FOCUS", False); + _x.losefocus = XInternAtom(_x.display, "_9WM_LOSE_FOCUS", False); + _x.wmprotos = XInternAtom(_x.display, "WM_PROTOCOLS", False); + } + + atoms[0] = _x.takefocus; + atoms[1] = _x.losefocus; + XChangeProperty(_x.display, w->drawable, _x.wmprotos, XA_ATOM, 32, + PropModeReplace, (uchar*)atoms, 2); + + /* + * Put the window on the screen, check to see what size we actually got. + */ + XMapWindow(_x.display, w->drawable); + XSync(_x.display, False); + + if(!XGetWindowAttributes(_x.display, w->drawable, &wattr)) + fprint(2, "XGetWindowAttributes failed\n"); + else if(wattr.width && wattr.height){ + if(wattr.width != Dx(r) || wattr.height != Dy(r)){ + r.max.x = wattr.width; + r.max.y = wattr.height; + } + }else + fprint(2, "XGetWindowAttributes: bad attrs\n"); + + /* + * Allocate our local backing store. + */ + w->screenr = r; + w->screenpm = XCreatePixmap(_x.display, w->drawable, Dx(r), Dy(r), _x.depth); + w->nextscreenpm = w->screenpm; + w->screenimage = _xallocmemimage(r, _x.chan, w->screenpm); + client->mouserect = r; + + /* + * Allocate some useful graphics contexts for the future. + * These can be used with any drawable matching w->drawable's + * pixel format (which is all the drawables we create). + */ + if(_x.gcfill == 0) { + _x.gcfill = xgc(w->screenpm, FillSolid, -1); + _x.gccopy = xgc(w->screenpm, -1, -1); + _x.gcsimplesrc = xgc(w->screenpm, FillStippled, -1); + _x.gczero = xgc(w->screenpm, -1, -1); + _x.gcreplsrc = xgc(w->screenpm, FillTiled, -1); + + pmid = XCreatePixmap(_x.display, w->drawable, 1, 1, 1); + _x.gcfill0 = xgc(pmid, FillSolid, 0); + _x.gccopy0 = xgc(pmid, -1, -1); + _x.gcsimplesrc0 = xgc(pmid, FillStippled, -1); + _x.gczero0 = xgc(pmid, -1, -1); + _x.gcreplsrc0 = xgc(pmid, FillTiled, -1); + XFreePixmap(_x.display, pmid); + } + + return w->screenimage; +} + +Memimage* +rpc_attach(Client *client, char *label, char *winsize) +{ + Memimage *m; + + xlock(); + m = xattach(client, label, winsize); + xunlock(); + return m; +} + +void +rpc_setlabel(Client *client, char *label) +{ + Xwin *w = (Xwin*)client->view; + XTextProperty name; + + /* + * Label and other properties required by ICCCCM. + */ + xlock(); + memset(&name, 0, sizeof name); + if(label == nil) + label = "pjw-face-here"; + name.value = (uchar*)label; + name.encoding = XA_STRING; + name.format = 8; + name.nitems = strlen((char*)name.value); + + XSetWMProperties( + _x.display, /* display */ + w->drawable, /* window */ + &name, /* XA_WM_NAME property */ + &name, /* XA_WM_ICON_NAME property */ + nil, /* XA_WM_COMMAND */ + 0, /* argc */ + nil, /* XA_WM_NORMAL_HINTS */ + nil, /* XA_WM_HINTS */ + nil /* XA_WM_CLASSHINTS */ + ); + XFlush(_x.display); + xunlock(); +} + +/* + * Create a GC with a particular fill style and XXX. + * Disable generation of GraphicsExpose/NoExpose events in the GC. + */ +static XGC +xgc(XDrawable d, int fillstyle, int foreground) +{ + XGC gc; + XGCValues v; + + memset(&v, 0, sizeof v); + v.function = GXcopy; + v.graphics_exposures = False; + gc = XCreateGC(_x.display, d, GCFunction|GCGraphicsExposures, &v); + if(fillstyle != -1) + XSetFillStyle(_x.display, gc, fillstyle); + if(foreground != -1) + XSetForeground(_x.display, gc, 0); + return gc; +} + + +/* + * Initialize map with the Plan 9 rgbv color map. + */ +static void +plan9cmap(void) +{ + int r, g, b, cr, cg, cb, v, num, den, idx, v7, idx7; + static int once; + + if(once) + return; + once = 1; + + for(r=0; r!=4; r++) + for(g = 0; g != 4; g++) + for(b = 0; b!=4; b++) + for(v = 0; v!=4; v++){ + den=r; + if(g > den) + den=g; + if(b > den) + den=b; + /* divide check -- pick grey shades */ + if(den==0) + cr=cg=cb=v*17; + else { + num=17*(4*den+v); + cr=r*num/den; + cg=g*num/den; + cb=b*num/den; + } + idx = r*64 + v*16 + ((g*4 + b + v - r) & 15); + _x.map[idx].red = cr*0x0101; + _x.map[idx].green = cg*0x0101; + _x.map[idx].blue = cb*0x0101; + _x.map[idx].pixel = idx; + _x.map[idx].flags = DoRed|DoGreen|DoBlue; + + v7 = v >> 1; + idx7 = r*32 + v7*16 + g*4 + b; + if((v & 1) == v7){ + _x.map7to8[idx7][0] = idx; + if(den == 0) { /* divide check -- pick grey shades */ + cr = ((255.0/7.0)*v7)+0.5; + cg = cr; + cb = cr; + } + else { + num=17*15*(4*den+v7*2)/14; + cr=r*num/den; + cg=g*num/den; + cb=b*num/den; + } + _x.map7[idx7].red = cr*0x0101; + _x.map7[idx7].green = cg*0x0101; + _x.map7[idx7].blue = cb*0x0101; + _x.map7[idx7].pixel = idx7; + _x.map7[idx7].flags = DoRed|DoGreen|DoBlue; + } + else + _x.map7to8[idx7][1] = idx; + } +} + +/* + * Initialize and install the rgbv color map as a private color map + * for this application. It gets the best colors when it has the + * cursor focus. + * + * We always choose the best depth possible, but that might not + * be the default depth. On such "suboptimal" systems, we have to allocate an + * empty color map anyway, according to Axel Belinfante. + */ +static int +setupcmap(XWindow w) +{ + char buf[30]; + int i; + u32int p, pp; + XColor c; + + if(_x.depth <= 1) + return 0; + + if(_x.depth >= 24) { + if(_x.usetable == 0) + _x.cmap = XCreateColormap(_x.display, w, _x.vis, AllocNone); + + /* + * The pixel value returned from XGetPixel needs to + * be converted to RGB so we can call rgb2cmap() + * to translate between 24 bit X and our color. Unfortunately, + * the return value appears to be display server endian + * dependant. Therefore, we run some heuristics to later + * determine how to mask the int value correctly. + * Yeah, I know we can look at _x.vis->byte_order but + * some displays say MSB even though they run on LSB. + * Besides, this is more anal. + */ + c = _x.map[19]; /* known to have different R, G, B values */ + if(!XAllocColor(_x.display, _x.cmap, &c)){ + werrstr("XAllocColor: %r"); + return -1; + } + p = c.pixel; + pp = rgb2cmap((p>>16)&0xff,(p>>8)&0xff,p&0xff); + if(pp != _x.map[19].pixel) { + /* check if endian is other way */ + pp = rgb2cmap(p&0xff,(p>>8)&0xff,(p>>16)&0xff); + if(pp != _x.map[19].pixel){ + werrstr("cannot detect X server byte order"); + return -1; + } + + switch(_x.chan){ + case RGB24: + _x.chan = BGR24; + break; + case XRGB32: + _x.chan = XBGR32; + break; + default: + werrstr("cannot byteswap channel %s", + chantostr(buf, _x.chan)); + break; + } + } + }else if(_x.vis->class == TrueColor || _x.vis->class == DirectColor){ + /* + * Do nothing. We have no way to express a + * mixed-endian 16-bit screen, so pretend they don't exist. + */ + if(_x.usetable == 0) + _x.cmap = XCreateColormap(_x.display, w, _x.vis, AllocNone); + }else if(_x.vis->class == PseudoColor){ + if(_x.usetable == 0){ + _x.cmap = XCreateColormap(_x.display, w, _x.vis, AllocAll); + XStoreColors(_x.display, _x.cmap, _x.map, 256); + for(i = 0; i < 256; i++){ + _x.tox11[i] = i; + _x.toplan9[i] = i; + } + }else{ + for(i = 0; i < 128; i++){ + c = _x.map7[i]; + if(!XAllocColor(_x.display, _x.cmap, &c)){ + werrstr("can't allocate colors in 7-bit map"); + return -1; + } + _x.tox11[_x.map7to8[i][0]] = c.pixel; + _x.tox11[_x.map7to8[i][1]] = c.pixel; + _x.toplan9[c.pixel] = _x.map7to8[i][0]; + } + } + }else{ + werrstr("unsupported visual class %d", _x.vis->class); + return -1; + } + return 0; +} + +void +rpc_shutdown(void) +{ +} + +void +rpc_flush(Client *client, Rectangle r) +{ + Xwin *w = (Xwin*)client->view; + + xlock(); + if(w->nextscreenpm != w->screenpm){ + XSync(_x.display, False); + XFreePixmap(_x.display, w->screenpm); + w->screenpm = w->nextscreenpm; + } + + if(r.min.x >= r.max.x || r.min.y >= r.max.y) { + xunlock(); + return; + } + + XCopyArea(_x.display, w->screenpm, w->drawable, _x.gccopy, r.min.x, r.min.y, + Dx(r), Dy(r), r.min.x, r.min.y); + XFlush(_x.display); + xunlock(); +} + +static void +_xexpose(Xwin *w, XEvent *e) +{ + XExposeEvent *xe; + Rectangle r; + + if(w->screenpm != w->nextscreenpm) + return; + xe = (XExposeEvent*)e; + r.min.x = xe->x; + r.min.y = xe->y; + r.max.x = xe->x+xe->width; + r.max.y = xe->y+xe->height; + XCopyArea(_x.display, w->screenpm, w->drawable, _x.gccopy, r.min.x, r.min.y, + Dx(r), Dy(r), r.min.x, r.min.y); + XSync(_x.display, False); +} + +static int +_xdestroy(Xwin *w, XEvent *e) +{ + XDestroyWindowEvent *xe; + + xe = (XDestroyWindowEvent*)e; + if(xe->window == w->drawable){ + w->destroyed = 1; + return 1; + } + return 0; +} + +static int +_xconfigure(Xwin *w, XEvent *e) +{ + Rectangle r; + XConfigureEvent *xe = (XConfigureEvent*)e; + + if(!w->fullscreen){ + int rx, ry; + XWindow xw; + if(XTranslateCoordinates(_x.display, w->drawable, DefaultRootWindow(_x.display), 0, 0, &rx, &ry, &xw)) + w->windowrect = Rect(rx, ry, rx+xe->width, ry+xe->height); + } + + if(xe->width == Dx(w->screenr) && xe->height == Dy(w->screenr)) + return 0; + r = Rect(0, 0, xe->width, xe->height); + + if(w->screenpm != w->nextscreenpm){ + XCopyArea(_x.display, w->screenpm, w->drawable, _x.gccopy, r.min.x, r.min.y, + Dx(r), Dy(r), r.min.x, r.min.y); + XSync(_x.display, False); + } + w->newscreenr = r; + return 1; +} + +static int +_xreplacescreenimage(Client *client) +{ + Memimage *m; + XDrawable pixmap; + Rectangle r; + Xwin *w; + + w = (Xwin*)client->view; + r = w->newscreenr; + pixmap = XCreatePixmap(_x.display, w->drawable, Dx(r), Dy(r), _x.depth); + m = _xallocmemimage(r, _x.chan, pixmap); + if(w->nextscreenpm != w->screenpm) + XFreePixmap(_x.display, w->nextscreenpm); + w->nextscreenpm = pixmap; + w->screenr = r; + client->mouserect = r; + gfx_replacescreenimage(client, m); + return 1; +} + +void +rpc_resizeimg(Client *client) +{ + xlock(); + _xreplacescreenimage(client); + xunlock(); +} + +void +rpc_gfxdrawlock(void) +{ + xlock(); +} + +void +rpc_gfxdrawunlock(void) +{ + xunlock(); +} +void +rpc_topwin(Client *client) +{ + Xwin *w = (Xwin*)client->view; + + xlock(); + XMapRaised(_x.display, w->drawable); + XSetInputFocus(_x.display, w->drawable, RevertToPointerRoot, + CurrentTime); + XFlush(_x.display); + xunlock(); +} + +void +rpc_resizewindow(Client *client, Rectangle r) +{ + Xwin *w = (Xwin*)client->view; + XWindowChanges e; + int value_mask; + + xlock(); + memset(&e, 0, sizeof e); + value_mask = CWX|CWY|CWWidth|CWHeight; + e.width = Dx(r); + e.height = Dy(r); + XConfigureWindow(_x.display, w->drawable, value_mask, &e); + XFlush(_x.display); + xunlock(); +} + +static void +_xmovewindow(Xwin *w, Rectangle r) +{ + XWindowChanges e; + int value_mask; + + memset(&e, 0, sizeof e); + value_mask = CWX|CWY|CWWidth|CWHeight; + e.x = r.min.x; + e.y = r.min.y; + e.width = Dx(r); + e.height = Dy(r); + XConfigureWindow(_x.display, w->drawable, value_mask, &e); + XFlush(_x.display); +} + +static int +_xtoplan9kbd(XEvent *e) +{ + KeySym k; + + if(e->xany.type != KeyPress) + return -1; + needstack(64*1024); /* X has some *huge* buffers in openobject */ + /* and they're even bigger on SuSE */ + XLookupString((XKeyEvent*)e,NULL,0,&k,NULL); + if(k == NoSymbol) + return -1; + + if(k&0xFF00){ + switch(k){ + case XK_BackSpace: + case XK_Tab: + case XK_Escape: + case XK_Delete: + case XK_KP_0: + case XK_KP_1: + case XK_KP_2: + case XK_KP_3: + case XK_KP_4: + case XK_KP_5: + case XK_KP_6: + case XK_KP_7: + case XK_KP_8: + case XK_KP_9: + case XK_KP_Divide: + case XK_KP_Multiply: + case XK_KP_Subtract: + case XK_KP_Add: + case XK_KP_Decimal: + k &= 0x7F; + break; + case XK_Linefeed: + k = '\r'; + break; + case XK_KP_Space: + k = ' '; + break; + case XK_Home: + case XK_KP_Home: + k = Khome; + break; + case XK_Left: + case XK_KP_Left: + k = Kleft; + break; + case XK_Up: + case XK_KP_Up: + k = Kup; + break; + case XK_Down: + case XK_KP_Down: + k = Kdown; + break; + case XK_Right: + case XK_KP_Right: + k = Kright; + break; + case XK_Page_Down: + case XK_KP_Page_Down: + k = Kpgdown; + break; + case XK_End: + case XK_KP_End: + k = Kend; + break; + case XK_Page_Up: + case XK_KP_Page_Up: + k = Kpgup; + break; + case XK_Insert: + case XK_KP_Insert: + k = Kins; + break; + case XK_KP_Enter: + case XK_Return: + k = '\n'; + break; + case XK_Alt_L: + case XK_Meta_L: /* Shift Alt on PCs */ + case XK_Alt_R: + case XK_Meta_R: /* Shift Alt on PCs */ + case XK_Multi_key: + return -1; + default: /* not ISO-1 or tty control */ + if(k>0xff) { + k = _p9keysym2ucs(k); + if(k==-1) return -1; + } + } + } + + /* Compensate for servers that call a minus a hyphen */ + if(k == XK_hyphen) + k = XK_minus; + /* Do control mapping ourselves if translator doesn't */ + if(e->xkey.state&ControlMask) + k &= 0x9f; + if(k == NoSymbol) { + return -1; + } + + return k+0; +} + +static int +_xtoplan9mouse(Xwin *w, XEvent *e, Mouse *m) +{ + int s; + XButtonEvent *be; + XMotionEvent *me; + + if(_x.putsnarf != _x.assertsnarf){ + _x.assertsnarf = _x.putsnarf; + XSetSelectionOwner(_x.display, XA_PRIMARY, w->drawable, CurrentTime); + if(_x.clipboard != None) + XSetSelectionOwner(_x.display, _x.clipboard, w->drawable, CurrentTime); + XFlush(_x.display); + } + + switch(e->type){ + case ButtonPress: + be = (XButtonEvent*)e; + + /* + * Fake message, just sent to make us announce snarf. + * Apparently state and button are 16 and 8 bits on + * the wire, since they are truncated by the time they + * get to us. + */ + if(be->send_event + && (~be->state&0xFFFF)==0 + && (~be->button&0xFF)==0) + return -1; + /* BUG? on mac need to inherit these from elsewhere? */ + m->xy.x = be->x; + m->xy.y = be->y; + s = be->state; + m->msec = be->time; + switch(be->button){ + case 1: + s |= Button1Mask; + break; + case 2: + s |= Button2Mask; + break; + case 3: + s |= Button3Mask; + break; + case 4: + s |= Button4Mask; + break; + case 5: + s |= Button5Mask; + break; + } + break; + case ButtonRelease: + be = (XButtonEvent*)e; + m->xy.x = be->x; + m->xy.y = be->y; + s = be->state; + m->msec = be->time; + switch(be->button){ + case 1: + s &= ~Button1Mask; + break; + case 2: + s &= ~Button2Mask; + break; + case 3: + s &= ~Button3Mask; + break; + case 4: + s &= ~Button4Mask; + break; + case 5: + s &= ~Button5Mask; + break; + } + break; + + case MotionNotify: + me = (XMotionEvent*)e; + s = me->state; + m->xy.x = me->x; + m->xy.y = me->y; + m->msec = me->time; + return 0; // do not set buttons + + default: + return -1; + } + + m->buttons = 0; + if(s & Button1Mask) + m->buttons |= 1; + if(s & Button2Mask) + m->buttons |= 2; + if(s & Button3Mask) + m->buttons |= 4; + if(s & Button4Mask) + m->buttons |= 8; + if(s & Button5Mask) + m->buttons |= 16; + return 0; +} + +void +rpc_setmouse(Client *client, Point p) +{ + Xwin *w = (Xwin*)client->view; + + xlock(); + XWarpPointer(_x.display, None, w->drawable, 0, 0, 0, 0, p.x, p.y); + XFlush(_x.display); + xunlock(); +} + +static int +revbyte(int b) +{ + int r; + + r = 0; + r |= (b&0x01) << 7; + r |= (b&0x02) << 5; + r |= (b&0x04) << 3; + r |= (b&0x08) << 1; + r |= (b&0x10) >> 1; + r |= (b&0x20) >> 3; + r |= (b&0x40) >> 5; + r |= (b&0x80) >> 7; + return r; +} + +static void +xcursorarrow(Xwin *w) +{ + if(_x.cursor != 0){ + XFreeCursor(_x.display, _x.cursor); + _x.cursor = 0; + } + XUndefineCursor(_x.display, w->drawable); + XFlush(_x.display); +} + + +void +rpc_setcursor(Client *client, Cursor *c, Cursor2 *c2) +{ + Xwin *w = (Xwin*)client->view; + XColor fg, bg; + XCursor xc; + Pixmap xsrc, xmask; + int i; + uchar src[2*16], mask[2*16]; + + USED(c2); + + xlock(); + if(c == nil){ + xcursorarrow(w); + xunlock(); + return; + } + for(i=0; i<2*16; i++){ + src[i] = revbyte(c->set[i]); + mask[i] = revbyte(c->set[i] | c->clr[i]); + } + + fg = _x.map[0]; + bg = _x.map[255]; + xsrc = XCreateBitmapFromData(_x.display, w->drawable, (char*)src, 16, 16); + xmask = XCreateBitmapFromData(_x.display, w->drawable, (char*)mask, 16, 16); + xc = XCreatePixmapCursor(_x.display, xsrc, xmask, &fg, &bg, -c->offset.x, -c->offset.y); + if(xc != 0) { + XDefineCursor(_x.display, w->drawable, xc); + if(_x.cursor != 0) + XFreeCursor(_x.display, _x.cursor); + _x.cursor = xc; + } + XFreePixmap(_x.display, xsrc); + XFreePixmap(_x.display, xmask); + XFlush(_x.display); + xunlock(); +} + +struct { + QLock lk; + char buf[SnarfSize]; +#ifdef APPLESNARF + Rune rbuf[SnarfSize]; + PasteboardRef apple; +#endif +} clip; + +static uchar* +_xgetsnarffrom(Xwin *w, XWindow xw, Atom clipboard, Atom target, int timeout0, int timeout) +{ + Atom prop, type; + ulong len, lastlen, dummy; + int fmt, i; + uchar *data, *xdata; + + /* + * We should be waiting for SelectionNotify here, but it might never + * come, and we have no way to time out. Instead, we will clear + * local property #1, request our buddy to fill it in for us, and poll + * until he's done or we get tired of waiting. + */ + prop = 1; + XChangeProperty(_x.display, w->drawable, prop, target, 8, PropModeReplace, (uchar*)"", 0); + XConvertSelection(_x.display, clipboard, target, prop, w->drawable, CurrentTime); + XFlush(_x.display); + lastlen = 0; + timeout0 = (timeout0 + 9)/10; + timeout = (timeout + 9)/10; + for(i=0; i<timeout0 || (lastlen!=0 && i<timeout); i++){ + usleep(10*1000); + XGetWindowProperty(_x.display, w->drawable, prop, 0, 0, 0, AnyPropertyType, + &type, &fmt, &dummy, &len, &xdata); + if(lastlen == len && len > 0){ + XFree(xdata); + break; + } + lastlen = len; + XFree(xdata); + } + if(len == 0) + return nil; + + /* get the property */ + xdata = nil; + XGetWindowProperty(_x.display, w->drawable, prop, 0, SnarfSize/sizeof(ulong), 0, + AnyPropertyType, &type, &fmt, &len, &dummy, &xdata); + if((type != target && type != XA_STRING && type != _x.utf8string) || len == 0){ + if(xdata) + XFree(xdata); + return nil; + } + if(xdata){ + data = (uchar*)strdup((char*)xdata); + XFree(xdata); + return data; + } + return nil; +} + +char* +rpc_getsnarf(void) +{ + uchar *data; + Atom clipboard; + XWindow xw; + Xwin *w; + + qlock(&clip.lk); + xlock(); + w = _x.windows; + /* + * Have we snarfed recently and the X server hasn't caught up? + */ + if(_x.putsnarf != _x.assertsnarf) + goto mine; + + /* + * Is there a primary selection (highlighted text in an xterm)? + */ + clipboard = XA_PRIMARY; + xw = XGetSelectionOwner(_x.display, XA_PRIMARY); + // TODO check more + if(xw == w->drawable){ + mine: + data = (uchar*)strdup(clip.buf); + goto out; + } + + /* + * If not, is there a clipboard selection? + */ + if(xw == None && _x.clipboard != None){ + clipboard = _x.clipboard; + xw = XGetSelectionOwner(_x.display, _x.clipboard); + if(xw == w->drawable) + goto mine; + } + + /* + * If not, give up. + */ + if(xw == None){ + data = nil; + goto out; + } + + if((data = _xgetsnarffrom(w, xw, clipboard, _x.utf8string, 10, 100)) == nil) + if((data = _xgetsnarffrom(w, xw, clipboard, XA_STRING, 10, 100)) == nil){ + /* nothing left to do */ + } + +out: + xunlock(); + qunlock(&clip.lk); + return (char*)data; +} + +void +__xputsnarf(char *data) +{ + XButtonEvent e; + Xwin *w; + + if(strlen(data) >= SnarfSize) + return; + qlock(&clip.lk); + xlock(); + w = _x.windows; + strcpy(clip.buf, data); + /* leave note for mouse proc to assert selection ownership */ + _x.putsnarf++; + + /* send mouse a fake event so snarf is announced */ + memset(&e, 0, sizeof e); + e.type = ButtonPress; + e.window = w->drawable; + e.state = ~0; + e.button = ~0; + XSendEvent(_x.display, w->drawable, True, ButtonPressMask, (XEvent*)&e); + XFlush(_x.display); + xunlock(); + qunlock(&clip.lk); +} + +static int +_xselect(XEvent *e) +{ + char *name; + XEvent r; + XSelectionRequestEvent *xe; + Atom a[4]; + + memset(&r, 0, sizeof r); + xe = (XSelectionRequestEvent*)e; +if(0) fprint(2, "xselect target=%d requestor=%d property=%d selection=%d (sizeof atom=%d)\n", + xe->target, xe->requestor, xe->property, xe->selection, sizeof a[0]); + r.xselection.property = xe->property; + if(xe->target == _x.targets){ + a[0] = _x.utf8string; + a[1] = XA_STRING; + a[2] = _x.text; + a[3] = _x.compoundtext; + XChangeProperty(_x.display, xe->requestor, xe->property, XA_ATOM, + 32, PropModeReplace, (uchar*)a, nelem(a)); + }else if(xe->target == XA_STRING + || xe->target == _x.utf8string + || xe->target == _x.text + || xe->target == _x.compoundtext + || ((name = XGetAtomName(_x.display, xe->target)) && strcmp(name, "text/plain;charset=UTF-8") == 0)){ + /* text/plain;charset=UTF-8 seems nonstandard but is used by Synergy */ + /* if the target is STRING we're supposed to reply with Latin1 XXX */ + qlock(&clip.lk); + XChangeProperty(_x.display, xe->requestor, xe->property, xe->target, + 8, PropModeReplace, (uchar*)clip.buf, strlen(clip.buf)); + qunlock(&clip.lk); + }else{ + if(strcmp(name, "TIMESTAMP") != 0) + fprint(2, "%s: cannot handle selection request for '%s' (%d)\n", argv0, name, (int)xe->target); + r.xselection.property = None; + } + + r.xselection.display = xe->display; + /* r.xselection.property filled above */ + r.xselection.target = xe->target; + r.xselection.type = SelectionNotify; + r.xselection.requestor = xe->requestor; + r.xselection.time = xe->time; + r.xselection.send_event = True; + r.xselection.selection = xe->selection; + XSendEvent(_x.display, xe->requestor, False, 0, &r); + XFlush(_x.display); + return 0; +} + +#ifdef APPLESNARF +char* +_applegetsnarf(void) +{ + char *s, *t; + CFArrayRef flavors; + CFDataRef data; + CFIndex nflavor, ndata, j; + CFStringRef type; + ItemCount nitem; + PasteboardItemID id; + PasteboardSyncFlags flags; + UInt32 i; + +/* fprint(2, "applegetsnarf\n"); */ + qlock(&clip.lk); + if(clip.apple == nil){ + if(PasteboardCreate(kPasteboardClipboard, &clip.apple) != noErr){ + fprint(2, "apple pasteboard create failed\n"); + qunlock(&clip.lk); + return nil; + } + } + flags = PasteboardSynchronize(clip.apple); + if(flags&kPasteboardClientIsOwner){ + s = strdup(clip.buf); + qunlock(&clip.lk); + return s; + } + if(PasteboardGetItemCount(clip.apple, &nitem) != noErr){ + fprint(2, "apple pasteboard get item count failed\n"); + qunlock(&clip.lk); + return nil; + } + for(i=1; i<=nitem; i++){ + if(PasteboardGetItemIdentifier(clip.apple, i, &id) != noErr) + continue; + if(PasteboardCopyItemFlavors(clip.apple, id, &flavors) != noErr) + continue; + nflavor = CFArrayGetCount(flavors); + for(j=0; j<nflavor; j++){ + type = (CFStringRef)CFArrayGetValueAtIndex(flavors, j); + if(!UTTypeConformsTo(type, CFSTR("public.utf16-plain-text"))) + continue; + if(PasteboardCopyItemFlavorData(clip.apple, id, type, &data) != noErr) + continue; + ndata = CFDataGetLength(data); + qunlock(&clip.lk); + s = smprint("%.*S", ndata/2, (Rune*)CFDataGetBytePtr(data)); + CFRelease(flavors); + CFRelease(data); + for(t=s; *t; t++) + if(*t == '\r') + *t = '\n'; + return s; + } + CFRelease(flavors); + } + qunlock(&clip.lk); + return nil; +} + +void +_appleputsnarf(char *s) +{ + CFDataRef cfdata; + PasteboardSyncFlags flags; + +/* fprint(2, "appleputsnarf\n"); */ + + if(strlen(s) >= SnarfSize) + return; + qlock(&clip.lk); + strcpy(clip.buf, s); + runesnprint(clip.rbuf, nelem(clip.rbuf), "%s", s); + if(clip.apple == nil){ + if(PasteboardCreate(kPasteboardClipboard, &clip.apple) != noErr){ + fprint(2, "apple pasteboard create failed\n"); + qunlock(&clip.lk); + return; + } + } + if(PasteboardClear(clip.apple) != noErr){ + fprint(2, "apple pasteboard clear failed\n"); + qunlock(&clip.lk); + return; + } + flags = PasteboardSynchronize(clip.apple); + if((flags&kPasteboardModified) || !(flags&kPasteboardClientIsOwner)){ + fprint(2, "apple pasteboard cannot assert ownership\n"); + qunlock(&clip.lk); + return; + } + cfdata = CFDataCreate(kCFAllocatorDefault, + (uchar*)clip.rbuf, runestrlen(clip.rbuf)*2); + if(cfdata == nil){ + fprint(2, "apple pasteboard cfdatacreate failed\n"); + qunlock(&clip.lk); + return; + } + if(PasteboardPutItemFlavor(clip.apple, (PasteboardItemID)1, + CFSTR("public.utf16-plain-text"), cfdata, 0) != noErr){ + fprint(2, "apple pasteboard putitem failed\n"); + CFRelease(cfdata); + qunlock(&clip.lk); + return; + } + /* CFRelease(cfdata); ??? */ + qunlock(&clip.lk); +} +#endif /* APPLESNARF */ + +void +rpc_putsnarf(char *data) +{ +#ifdef APPLESNARF + _appleputsnarf(data); +#endif + __xputsnarf(data); +} + +/* + * Send the mouse event back to the window manager. + * So that 9term can tell rio to pop up its button3 menu. + */ +void +rpc_bouncemouse(Client *c, Mouse m) +{ + Xwin *w = (Xwin*)c->view; + XButtonEvent e; + XWindow dw; + + xlock(); + e.type = ButtonPress; + e.state = 0; + e.button = 0; + if(m.buttons&1) + e.button = 1; + else if(m.buttons&2) + e.button = 2; + else if(m.buttons&4) + e.button = 3; + e.same_screen = 1; + XTranslateCoordinates(_x.display, w->drawable, + DefaultRootWindow(_x.display), + m.xy.x, m.xy.y, &e.x_root, &e.y_root, &dw); + e.root = DefaultRootWindow(_x.display); + e.window = e.root; + e.subwindow = None; + e.x = e.x_root; + e.y = e.y_root; +#undef time + e.time = CurrentTime; + XUngrabPointer(_x.display, m.msec); + XSendEvent(_x.display, e.root, True, ButtonPressMask, (XEvent*)&e); + XFlush(_x.display); + xunlock(); +} diff --git a/src/cmd/devdraw/x11-srv.c b/src/cmd/devdraw/x11-srv.c deleted file mode 100644 index 81416482..00000000 --- a/src/cmd/devdraw/x11-srv.c +++ /dev/null @@ -1,638 +0,0 @@ -/* - * Window system protocol server. - * Use select and a single proc and single stack - * to avoid aggravating the X11 library, which is - * subtle and quick to anger. - */ - -// #define SHOWEVENT - -#include <u.h> -#include <sys/select.h> -#include <errno.h> -#ifdef SHOWEVENT -#include <stdio.h> -#endif -#include "x11-inc.h" - -#include <libc.h> -#include <draw.h> -#include <memdraw.h> -#include <memlayer.h> -#include <keyboard.h> -#include <mouse.h> -#include <cursor.h> -#include <drawfcall.h> -#include "x11-memdraw.h" -#include "devdraw.h" - -#undef time - -#define MouseMask (\ - ButtonPressMask|\ - ButtonReleaseMask|\ - PointerMotionMask|\ - Button1MotionMask|\ - Button2MotionMask|\ - Button3MotionMask) - -#define Mask MouseMask|ExposureMask|StructureNotifyMask|KeyPressMask|KeyReleaseMask|EnterWindowMask|LeaveWindowMask|FocusChangeMask - -typedef struct Kbdbuf Kbdbuf; -typedef struct Mousebuf Mousebuf; -typedef struct Fdbuf Fdbuf; -typedef struct Tagbuf Tagbuf; - -struct Kbdbuf -{ - Rune r[32]; - int ri; - int wi; - int stall; -}; - -struct Mousebuf -{ - Mouse m[32]; - int ri; - int wi; - int stall; - int resized; -}; - -struct Tagbuf -{ - int t[32]; - int ri; - int wi; -}; - -struct Fdbuf -{ - uchar buf[2*MAXWMSG]; - uchar *rp; - uchar *wp; - uchar *ep; -}; - -Kbdbuf kbd; -Mousebuf mouse; -Fdbuf fdin; -Fdbuf fdout; -Tagbuf kbdtags; -Tagbuf mousetags; - -void fdslide(Fdbuf*); -void runmsg(Wsysmsg*); -void replymsg(Wsysmsg*); -void runxevent(XEvent*); -void matchkbd(void); -void matchmouse(void); -int fdnoblock(int); - -int chatty; -int drawsleep; -int fullscreen; - -Rectangle windowrect; -Rectangle screenrect; - -void -usage(void) -{ - fprint(2, "usage: devdraw (don't run directly)\n"); - exits("usage"); -} - -void -bell(void *v, char *msg) -{ - if(strcmp(msg, "alarm") == 0) - drawsleep = drawsleep ? 0 : 1000; - noted(NCONT); -} - -void -main(int argc, char **argv) -{ - int n, top, firstx; - fd_set rd, wr, xx; - Wsysmsg m; - XEvent event; - - /* - * Move the protocol off stdin/stdout so that - * any inadvertent prints don't screw things up. - */ - dup(0, 3); - dup(1, 4); - close(0); - close(1); - open("/dev/null", OREAD); - open("/dev/null", OWRITE); - - /* reopens stdout if debugging */ - runxevent(0); - - fmtinstall('W', drawfcallfmt); - - ARGBEGIN{ - case 'D': - chatty++; - break; - default: - usage(); - }ARGEND - - /* - * Ignore arguments. They're only for good ps -a listings. - */ - - notify(bell); - - fdin.rp = fdin.wp = fdin.buf; - fdin.ep = fdin.buf+sizeof fdin.buf; - - fdout.rp = fdout.wp = fdout.buf; - fdout.ep = fdout.buf+sizeof fdout.buf; - - fdnoblock(3); - fdnoblock(4); - - firstx = 1; - _x.fd = -1; - for(;;){ - /* set up file descriptors */ - FD_ZERO(&rd); - FD_ZERO(&wr); - FD_ZERO(&xx); - /* - * Don't read unless there's room *and* we haven't - * already filled the output buffer too much. - */ - if(fdout.wp < fdout.buf+MAXWMSG && fdin.wp < fdin.ep) - FD_SET(3, &rd); - if(fdout.wp > fdout.rp) - FD_SET(4, &wr); - FD_SET(3, &xx); - FD_SET(4, &xx); - top = 4; - if(_x.fd >= 0){ - if(firstx){ - firstx = 0; - XSelectInput(_x.display, _x.drawable, Mask); - } - FD_SET(_x.fd, &rd); - FD_SET(_x.fd, &xx); - XFlush(_x.display); - if(_x.fd > top) - top = _x.fd; - } - - if(chatty) - fprint(2, "select %d...\n", top+1); - /* wait for something to happen */ - again: - if(select(top+1, &rd, &wr, &xx, NULL) < 0){ - if(errno == EINTR) - goto again; - if(chatty) - fprint(2, "select failure\n"); - exits(0); - } - if(chatty) - fprint(2, "got select...\n"); - - { - /* read what we can */ - n = 1; - while(fdin.wp < fdin.ep && (n = read(3, fdin.wp, fdin.ep-fdin.wp)) > 0) - fdin.wp += n; - if(n == 0){ - if(chatty) - fprint(2, "eof\n"); - exits(0); - } - if(n < 0 && errno != EAGAIN) - sysfatal("reading wsys msg: %r"); - - /* pick off messages one by one */ - while((n = convM2W(fdin.rp, fdin.wp-fdin.rp, &m)) > 0){ - /* fprint(2, "<- %W\n", &m); */ - runmsg(&m); - fdin.rp += n; - } - - /* slide data to beginning of buf */ - fdslide(&fdin); - } - { - /* write what we can */ - n = 1; - while(fdout.rp < fdout.wp && (n = write(4, fdout.rp, fdout.wp-fdout.rp)) > 0) - fdout.rp += n; - if(n == 0) - sysfatal("short write writing wsys"); - if(n < 0 && errno != EAGAIN) - sysfatal("writing wsys msg: %r"); - - /* slide data to beginning of buf */ - fdslide(&fdout); - } - { - /* - * Read an X message if we can. - * (XPending actually calls select to make sure - * the display's fd is readable and then reads - * in any waiting data before declaring whether - * there are events on the queue.) - */ - while(XPending(_x.display)){ - XNextEvent(_x.display, &event); - runxevent(&event); - } - } - } -} - -int -fdnoblock(int fd) -{ - return fcntl(fd, F_SETFL, fcntl(fd, F_GETFL)|O_NONBLOCK); -} - -void -fdslide(Fdbuf *fb) -{ - int n; - - n = fb->wp - fb->rp; - if(n > 0) - memmove(fb->buf, fb->rp, n); - fb->rp = fb->buf; - fb->wp = fb->rp+n; -} - -void -replyerror(Wsysmsg *m) -{ - char err[256]; - - rerrstr(err, sizeof err); - m->type = Rerror; - m->error = err; - replymsg(m); -} - - - -/* - * Handle a single wsysmsg. - * Might queue for later (kbd, mouse read) - */ -void -runmsg(Wsysmsg *m) -{ - uchar buf[65536]; - int n; - Memimage *i; - - switch(m->type){ - case Tinit: - memimageinit(); - i = _xattach(m->label, m->winsize); - _initdisplaymemimage(i); - replymsg(m); - break; - - case Trdmouse: - mousetags.t[mousetags.wi++] = m->tag; - if(mousetags.wi == nelem(mousetags.t)) - mousetags.wi = 0; - if(mousetags.wi == mousetags.ri) - sysfatal("too many queued mouse reads"); - /* fprint(2, "mouse unstall\n"); */ - mouse.stall = 0; - matchmouse(); - break; - - case Trdkbd: - kbdtags.t[kbdtags.wi++] = m->tag; - if(kbdtags.wi == nelem(kbdtags.t)) - kbdtags.wi = 0; - if(kbdtags.wi == kbdtags.ri) - sysfatal("too many queued keyboard reads"); - kbd.stall = 0; - matchkbd(); - break; - - case Tmoveto: - _xmoveto(m->mouse.xy); - replymsg(m); - break; - - case Tcursor: - case Tcursor2: - if(m->arrowcursor) - _xsetcursor(nil); - else - _xsetcursor(&m->cursor); - replymsg(m); - break; - - case Tbouncemouse: - _xbouncemouse(&m->mouse); - replymsg(m); - break; - - case Tlabel: - _xsetlabel(m->label); - replymsg(m); - break; - - case Trdsnarf: - m->snarf = _xgetsnarf(); - replymsg(m); - free(m->snarf); - break; - - case Twrsnarf: - _xputsnarf(m->snarf); - replymsg(m); - break; - - case Trddraw: - n = m->count; - if(n > sizeof buf) - n = sizeof buf; - n = _drawmsgread(buf, n); - if(n < 0) - replyerror(m); - else{ - m->count = n; - m->data = buf; - replymsg(m); - } - break; - - case Twrdraw: - if(_drawmsgwrite(m->data, m->count) < 0) - replyerror(m); - else - replymsg(m); - break; - - case Ttop: - _xtopwindow(); - replymsg(m); - break; - - case Tresize: - _xresizewindow(m->rect); - replymsg(m); - break; - } -} - -/* - * Reply to m. - */ -void -replymsg(Wsysmsg *m) -{ - int n; - - /* T -> R msg */ - if(m->type%2 == 0) - m->type++; - - /* fprint(2, "-> %W\n", m); */ - /* copy to output buffer */ - n = sizeW2M(m); - if(fdout.wp+n > fdout.ep) - sysfatal("out of space for reply message"); - convW2M(m, fdout.wp, n); - fdout.wp += n; -} - -/* - * Match queued kbd reads with queued kbd characters. - */ -void -matchkbd(void) -{ - Wsysmsg m; - - if(kbd.stall) - return; - while(kbd.ri != kbd.wi && kbdtags.ri != kbdtags.wi){ - m.type = Rrdkbd; - m.tag = kbdtags.t[kbdtags.ri++]; - if(kbdtags.ri == nelem(kbdtags.t)) - kbdtags.ri = 0; - m.rune = kbd.r[kbd.ri++]; - if(kbd.ri == nelem(kbd.r)) - kbd.ri = 0; - replymsg(&m); - } -} - -/* - * Match queued mouse reads with queued mouse events. - */ -void -matchmouse(void) -{ - Wsysmsg m; - - while(mouse.ri != mouse.wi && mousetags.ri != mousetags.wi){ - m.type = Rrdmouse; - m.tag = mousetags.t[mousetags.ri++]; - if(mousetags.ri == nelem(mousetags.t)) - mousetags.ri = 0; - m.mouse = mouse.m[mouse.ri]; - m.resized = mouse.resized; - /* - if(m.resized) - fprint(2, "sending resize\n"); - */ - mouse.resized = 0; - mouse.ri++; - if(mouse.ri == nelem(mouse.m)) - mouse.ri = 0; - replymsg(&m); - } -} - -static int kbuttons; -static int altdown; -static int kstate; - -static void -sendmouse(Mouse m) -{ - m.buttons |= kbuttons; - mouse.m[mouse.wi] = m; - mouse.wi++; - if(mouse.wi == nelem(mouse.m)) - mouse.wi = 0; - if(mouse.wi == mouse.ri){ - mouse.stall = 1; - mouse.ri = 0; - mouse.wi = 1; - mouse.m[0] = m; - /* fprint(2, "mouse stall\n"); */ - } - matchmouse(); -} - -/* - * Handle an incoming X event. - */ -void -runxevent(XEvent *xev) -{ - int c; - KeySym k; - static Mouse m; - XButtonEvent *be; - XKeyEvent *ke; - -#ifdef SHOWEVENT - static int first = 1; - if(first){ - dup(create("/tmp/devdraw.out", OWRITE, 0666), 1); - setbuf(stdout, 0); - first = 0; - } -#endif - - if(xev == 0) - return; - -#ifdef SHOWEVENT - print("\n"); - ShowEvent(xev); -#endif - - switch(xev->type){ - case Expose: - _xexpose(xev); - break; - - case DestroyNotify: - if(_xdestroy(xev)) - exits(0); - break; - - case ConfigureNotify: - if(_xconfigure(xev)){ - mouse.resized = 1; - _xreplacescreenimage(); - sendmouse(m); - } - break; - - case ButtonPress: - be = (XButtonEvent*)xev; - if(be->button == 1) { - if(kstate & ControlMask) - be->button = 2; - else if(kstate & Mod1Mask) - be->button = 3; - } - // fall through - case ButtonRelease: - altdown = 0; - // fall through - case MotionNotify: - if(mouse.stall) - return; - if(_xtoplan9mouse(xev, &m) < 0) - return; - sendmouse(m); - break; - - case KeyRelease: - case KeyPress: - ke = (XKeyEvent*)xev; - XLookupString(ke, NULL, 0, &k, NULL); - c = ke->state; - switch(k) { - case XK_Alt_L: - case XK_Meta_L: /* Shift Alt on PCs */ - case XK_Alt_R: - case XK_Meta_R: /* Shift Alt on PCs */ - case XK_Multi_key: - if(xev->type == KeyPress) - altdown = 1; - else if(altdown) { - altdown = 0; - sendalt(); - } - break; - } - - switch(k) { - case XK_Control_L: - if(xev->type == KeyPress) - c |= ControlMask; - else - c &= ~ControlMask; - goto kbutton; - case XK_Alt_L: - case XK_Shift_L: - if(xev->type == KeyPress) - c |= Mod1Mask; - else - c &= ~Mod1Mask; - kbutton: - kstate = c; - if(m.buttons || kbuttons) { - altdown = 0; // used alt - kbuttons = 0; - if(c & ControlMask) - kbuttons |= 2; - if(c & Mod1Mask) - kbuttons |= 4; - sendmouse(m); - break; - } - } - - if(xev->type != KeyPress) - break; - if(k == XK_F11){ - fullscreen = !fullscreen; - _xmovewindow(fullscreen ? screenrect : windowrect); - return; - } - if(kbd.stall) - return; - if((c = _xtoplan9kbd(xev)) < 0) - return; - kbd.r[kbd.wi++] = c; - if(kbd.wi == nelem(kbd.r)) - kbd.wi = 0; - if(kbd.ri == kbd.wi) - kbd.stall = 1; - matchkbd(); - break; - - case FocusOut: - /* - * Some key combinations (e.g. Alt-Tab) can cause us - * to see the key down event without the key up event, - * so clear out the keyboard state when we lose the focus. - */ - kstate = 0; - altdown = 0; - abortcompose(); - break; - - case SelectionRequest: - _xselect(xev); - break; - } -} - diff --git a/src/cmd/devdraw/x11-unload.c b/src/cmd/devdraw/x11-unload.c index d01a232f..2f6241ba 100644 --- a/src/cmd/devdraw/x11-unload.c +++ b/src/cmd/devdraw/x11-unload.c @@ -12,4 +12,3 @@ unloadmemimage(Memimage *i, Rectangle r, uchar *data, int ndata) _xgetxdata(i, r); return _unloadmemimage(i, r, data, ndata); } - diff --git a/src/cmd/devdraw/x11-wsys.c b/src/cmd/devdraw/x11-wsys.c deleted file mode 100644 index 9095c950..00000000 --- a/src/cmd/devdraw/x11-wsys.c +++ /dev/null @@ -1,46 +0,0 @@ -#include <u.h> -#include "x11-inc.h" -#include <libc.h> -#include <draw.h> -#include <memdraw.h> -#include "x11-memdraw.h" - -void -_xtopwindow(void) -{ - XMapRaised(_x.display, _x.drawable); - XSetInputFocus(_x.display, _x.drawable, RevertToPointerRoot, - CurrentTime); - XFlush(_x.display); -} - -void -_xresizewindow(Rectangle r) -{ - XWindowChanges e; - int value_mask; - - memset(&e, 0, sizeof e); - value_mask = CWX|CWY|CWWidth|CWHeight; - e.width = Dx(r); - e.height = Dy(r); - XConfigureWindow(_x.display, _x.drawable, value_mask, &e); - XFlush(_x.display); -} - -void -_xmovewindow(Rectangle r) -{ - XWindowChanges e; - int value_mask; - - memset(&e, 0, sizeof e); - value_mask = CWX|CWY|CWWidth|CWHeight; - e.x = r.min.x; - e.y = r.min.y; - e.width = Dx(r); - e.height = Dy(r); - XConfigureWindow(_x.display, _x.drawable, value_mask, &e); - XFlush(_x.display); -} - |