sysret fix - better cpuid support - lcall support for x86_64 - efer access in i386...
[qemu/qemu_0_9_1_stable.git] / cocoa.m
blobd41517b08737f5b711792abf98c6580f7fdf8283
1 /*
2  * QEMU Cocoa display driver
3  * 
4  * Copyright (c) 2005 Pierre d'Herbemont
5  *                    many code/inspiration from SDL 1.2 code (LGPL)
6  * 
7  * Permission is hereby granted, free of charge, to any person obtaining a copy
8  * of this software and associated documentation files (the "Software"), to deal
9  * in the Software without restriction, including without limitation the rights
10  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11  * copies of the Software, and to permit persons to whom the Software is
12  * furnished to do so, subject to the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be included in
15  * all copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23  * THE SOFTWARE.
24  */
26     Todo :    x  miniaturize window 
27               x  center the window
28               -  save window position
29               -  handle keyboard event
30               -  handle mouse event
31               -  non 32 bpp support
32               -  full screen
33               -  mouse focus
34               x  simple graphical prompt to demo
35               -  better graphical prompt
38 #import <Cocoa/Cocoa.h>
40 #include "vl.h"
42 NSWindow *window = NULL;
43 NSQuickDrawView *qd_view = NULL;
46 int gArgc;
47 char **gArgv;
48 DisplayState current_ds;
50 /* main defined in qemu/vl.c */
51 int qemu_main(int argc, char **argv);
53 /* To deal with miniaturization */
54 @interface QemuWindow : NSWindow
55 { }
56 @end
60  ------------------------------------------------------
61     Qemu Video Driver
62  ------------------------------------------------------
66  ------------------------------------------------------
67     cocoa_update
68  ------------------------------------------------------
70 static void cocoa_update(DisplayState *ds, int x, int y, int w, int h)
72     //printf("updating x=%d y=%d w=%d h=%d\n", x, y, w, h);
74     /* Use QDFlushPortBuffer() to flush content to display */
75     RgnHandle dirty = NewRgn ();
76     RgnHandle temp  = NewRgn ();
78     SetEmptyRgn (dirty);
80     /* Build the region of dirty rectangles */
81     MacSetRectRgn (temp, x, y,
82                         x + w, y + h);
83     MacUnionRgn (dirty, temp, dirty);
84                 
85     /* Flush the dirty region */
86     QDFlushPortBuffer ( [ qd_view  qdPort ], dirty );
87     DisposeRgn (dirty);
88     DisposeRgn (temp);
92  ------------------------------------------------------
93     cocoa_resize
94  ------------------------------------------------------
96 static void cocoa_resize(DisplayState *ds, int w, int h)
98     const int device_bpp = 32;
99     static void *screen_pixels;
100     static int  screen_pitch;
101     NSRect contentRect;
102     
103     //printf("resizing to %d %d\n", w, h);
104     
105     contentRect = NSMakeRect (0, 0, w, h);
106     if(window)
107     {
108         [window close];
109         [window release];
110     }
111     window = [ [ QemuWindow alloc ] initWithContentRect:contentRect
112                                   styleMask:NSTitledWindowMask|NSMiniaturizableWindowMask|NSClosableWindowMask
113                                   backing:NSBackingStoreBuffered defer:NO];
114     if(!window)
115     {
116         fprintf(stderr, "(cocoa) can't create window\n");
117         exit(1);
118     }
119     
120     if(qd_view)
121         [qd_view release];
122     
123     qd_view = [ [ NSQuickDrawView alloc ] initWithFrame:contentRect ];
124     
125     if(!qd_view)
126     {
127          fprintf(stderr, "(cocoa) can't create qd_view\n");
128         exit(1);
129     }
130     
131     [ window setAcceptsMouseMovedEvents:YES ];
132     [ window setTitle:@"Qemu" ];
133     [ window setReleasedWhenClosed:NO ];
134     
135     /* Set screen to black */
136     [ window setBackgroundColor: [NSColor blackColor] ];
137     
138     /* set window position */
139     [ window center ];
140     
141     [ qd_view setAutoresizingMask: NSViewWidthSizable | NSViewHeightSizable ];
142     [ [ window contentView ] addSubview:qd_view ];
143     [ qd_view release ];
144     [ window makeKeyAndOrderFront:nil ];
145     
146     /* Careful here, the window seems to have to be onscreen to do that */
147     LockPortBits ( [ qd_view qdPort ] );
148     screen_pixels = GetPixBaseAddr ( GetPortPixMap ( [ qd_view qdPort ] ) );
149     screen_pitch  = GetPixRowBytes ( GetPortPixMap ( [ qd_view qdPort ] ) );
150     UnlockPortBits ( [ qd_view qdPort ] );
151     { 
152             int vOffset = [ window frame ].size.height - 
153                 [ qd_view frame ].size.height - [ qd_view frame ].origin.y;
154             
155             int hOffset = [ qd_view frame ].origin.x;
156                     
157             screen_pixels += (vOffset * screen_pitch) + hOffset * (device_bpp/8);
158     }
159     ds->data = screen_pixels;
160     ds->linesize = screen_pitch;
161     ds->depth = device_bpp;
162     ds->width = w;
163     ds->height = h;
164     
165     current_ds = *ds;
169  ------------------------------------------------------
170     keymap conversion
171  ------------------------------------------------------
174 static int keymap[] =
176     30, //'a' 0x0
177     31,  //'s'
178     32,  //'d'
179     33,  //'f'
180     35,  //'h'
181     34,  //'g'
182     44,  //'z'
183     45,  //'x'
184     46,  //'c'
185     47,  //'v'
186     0,   // 0  0x0a
187     48,  //'b'
188     16,  //'q'
189     17,  //'w'
190     18,  //'e'
191     19,  //'r' 
192     21,  //'y' 0x10
193     20,  //'t'
194     2,  //'1'
195     3,  //'2'
196     4,  //'3'
197     5,  //'4'
198     7,  //'6'
199     6,  //'5'
200     0,  //'='
201     10,  //'9'
202     8,  //'7' 0x1A
203     0,  //'-' 
204     9,  //'8' 
205     11,  //'0' 
206     27,  //']' 
207     24,  //'o' 
208     22,  //'u' 0x20
209     26,  //'['
210     23,  //'i'
211     25,  //'p'
212     28,  //'\n'
213     38,  //'l'
214     36,  //'j'
215     40,  //'"'
216     37,  //'k'
217     39,  //';'
218     15,  //'\t' 0x30
219     0,  //' '
220     0,  //'`'
221     14,  //'<backspace>'
222     0,  //'' 0x34
223     0,  //'<esc>'
224     0,  //'<esc>'
225     /* Not completed to finish see http://www.libsdl.org/cgi/cvsweb.cgi/SDL12/src/video/quartz/SDL_QuartzKeys.h?rev=1.6&content-type=text/x-cvsweb-markup */
228 static int cocoa_keycode_to_qemu(int keycode)
230     if(sizeof(keymap) <= keycode)
231     {
232         printf("(cocoa) warning unknow keycode 0x%x\n", keycode);
233         return 0;
234     }
235     return keymap[keycode];
239  ------------------------------------------------------
240     cocoa_refresh
241  ------------------------------------------------------
243 static void cocoa_refresh(DisplayState *ds)
245     //printf("cocoa_refresh \n");
246     NSDate *distantPast;
247     NSEvent *event;
248     NSAutoreleasePool *pool;
249     int grab = 1;
250     
251     pool = [ [ NSAutoreleasePool alloc ] init ];
252     distantPast = [ NSDate distantPast ];
253     
254     if (is_active_console(vga_console)) 
255         vga_update_display();
256     do {
257         event = [ NSApp nextEventMatchingMask:NSAnyEventMask untilDate:distantPast
258                         inMode: NSDefaultRunLoopMode dequeue:YES ];
259         if (event != nil) {
260             switch ([event type]) {
261                 case NSKeyDown:
262                     if(grab)
263                     {
264                         int keycode = cocoa_keycode_to_qemu([event keyCode]);
265                         
266                         if (keycode & 0x80)
267                             kbd_put_keycode(0xe0);
268                         kbd_put_keycode(keycode & 0x7f);
269                     }
270                     break;
271                 case NSKeyUp:
272                     if(grab)
273                     {
274                         int keycode = cocoa_keycode_to_qemu([event keyCode]);
276                         if (keycode & 0x80)
277                             kbd_put_keycode(0xe0);
278                         kbd_put_keycode(keycode | 0x80);
279                     }
280                     break;
281                 case NSScrollWheel:
282                 
283                 case NSLeftMouseDown:
284                 case NSLeftMouseUp:
285                 
286                 case NSOtherMouseDown:
287                 case NSRightMouseDown:
288                 
289                 case NSOtherMouseUp:
290                 case NSRightMouseUp:
291                 
292                 case NSMouseMoved:
293                 case NSOtherMouseDragged:
294                 case NSRightMouseDragged:
295                 case NSLeftMouseDragged:
296                 
297                 default: [NSApp sendEvent:event];
298             }
299         }
300     } while(event != nil);
304  ------------------------------------------------------
305     cocoa_cleanup
306  ------------------------------------------------------
309 static void cocoa_cleanup(void) 
315  ------------------------------------------------------
316     cocoa_display_init
317  ------------------------------------------------------
320 void cocoa_display_init(DisplayState *ds, int full_screen)
322     ds->dpy_update = cocoa_update;
323     ds->dpy_resize = cocoa_resize;
324     ds->dpy_refresh = cocoa_refresh;
325     
326     cocoa_resize(ds, 640, 400);
327     
328     atexit(cocoa_cleanup);
332  ------------------------------------------------------
333     Interface with Cocoa
334  ------------------------------------------------------
339  ------------------------------------------------------
340     QemuWindow
341     Some trick from SDL to use miniwindow
342  ------------------------------------------------------
344 static void QZ_SetPortAlphaOpaque ()
345 {    
346     /* Assume 32 bit if( bpp == 32 )*/
347     if ( 1 ) {
348     
349         uint32_t    *pixels = (uint32_t*) current_ds.data;
350         uint32_t    rowPixels = current_ds.linesize / 4;
351         uint32_t    i, j;
352         
353         for (i = 0; i < current_ds.height; i++)
354             for (j = 0; j < current_ds.width; j++) {
355         
356                 pixels[ (i * rowPixels) + j ] |= 0xFF000000;
357             }
358     }
361 @implementation QemuWindow
362 - (void)miniaturize:(id)sender
364         
365     /* make the alpha channel opaque so anim won't have holes in it */
366     QZ_SetPortAlphaOpaque ();
367     
368     [ super miniaturize:sender ];
369     
371 - (void)display
372 {    
373     /* 
374         This method fires just before the window deminaturizes from the Dock.
375         
376         We'll save the current visible surface, let the window manager redraw any
377         UI elements, and restore the SDL surface. This way, no expose event 
378         is required, and the deminiaturize works perfectly.
379     */
380     
381     /* make sure pixels are fully opaque */
382     QZ_SetPortAlphaOpaque ();
383     
384     /* save current visible SDL surface */
385     [ self cacheImageInRect:[ qd_view frame ] ];
386     
387     /* let the window manager redraw controls, border, etc */
388     [ super display ];
389     
390     /* restore visible SDL surface */
391     [ self restoreCachedImage ];
394 @end
398  ------------------------------------------------------
399     QemuCocoaGUIController
400     NSApp's delegate - indeed main object
401  ------------------------------------------------------
404 @interface QemuCocoaGUIController : NSObject
407 - (void)applicationDidFinishLaunching: (NSNotification *) note;
408 - (void)applicationWillTerminate:(NSNotification *)aNotification;
410 - (void)openPanelDidEnd:(NSOpenPanel *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo;
412 - (void)startEmulationWithArgc:(int)argc argv:(char**)argv;
413 @end
415 @implementation QemuCocoaGUIController
416 /* Called when the internal event loop has just started running */
417 - (void)applicationDidFinishLaunching: (NSNotification *) note
420     /* Display an open dialog box if no argument were passed or
421        if qemu was launched from the finder ( the Finder passes "-psn" ) */
423     if( gArgc <= 1 || strncmp (gArgv[1], "-psn", 4) == 0)
424     {
425         NSOpenPanel *op = [[NSOpenPanel alloc] init];
426         
427         cocoa_resize(&current_ds, 640, 400);
428         
429         [op setPrompt:@"Boot image"];
430         
431         [op setMessage:@"Select the disk image you want to boot.\n\nHit the \"Cancel\" button to quit"];
432         
433         [op beginSheetForDirectory:nil file:nil types:[NSArray arrayWithObjects:@"img",@"iso",@"dmg",@"qcow",@"cow",@"cloop",@"vmdk",nil]
434               modalForWindow:window modalDelegate:self
435               didEndSelector:@selector(openPanelDidEnd:returnCode:contextInfo:) contextInfo:NULL];
436     }
437     else
438     {
439         /* or Launch Qemu, with the global args */
440         [self startEmulationWithArgc:gArgc argv:gArgv];
441     }
444 - (void)applicationWillTerminate:(NSNotification *)aNotification
446     printf("Application will terminate\n");
447     qemu_system_shutdown_request();
448     /* In order to avoid a crash */
449     exit(0);
452 - (void)openPanelDidEnd:(NSOpenPanel *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo
454     if(returnCode == NSCancelButton)
455     {
456         exit(0);
457     }
458     
459     if(returnCode == NSOKButton)
460     {
461         char *bin = "qemu";
462         char *img = (char*)[ [ sheet filename ] cString];
463         
464         char **argv = (char**)malloc( sizeof(char*)*3 );
465         
466         asprintf(&argv[0], "%s", bin);
467         asprintf(&argv[1], "-hda");
468         asprintf(&argv[2], "%s", img);
469         
470         printf("Using argc %d argv %s -hda %s\n", 3, bin, img);
471         
472         [self startEmulationWithArgc:3 argv:(char**)argv];
473     }
476 - (void)startEmulationWithArgc:(int)argc argv:(char**)argv
478     int status;
479     /* Launch Qemu */
480     printf("starting qemu...\n");
481     status = qemu_main (argc, argv);
482     exit(status);
484 @end
487  ------------------------------------------------------
488     Application Creation
489  ------------------------------------------------------
492 /* Dock Connection */
493 typedef struct CPSProcessSerNum
495         UInt32                lo;
496         UInt32                hi;
497 } CPSProcessSerNum;
499 extern OSErr    CPSGetCurrentProcess( CPSProcessSerNum *psn);
500 extern OSErr    CPSEnableForegroundOperation( CPSProcessSerNum *psn, UInt32 _arg2, UInt32 _arg3, UInt32 _arg4, UInt32 _arg5);
501 extern OSErr    CPSSetFrontProcess( CPSProcessSerNum *psn);
503 /* Menu Creation */
504 static void setApplicationMenu(void)
506     /* warning: this code is very odd */
507     NSMenu *appleMenu;
508     NSMenuItem *menuItem;
509     NSString *title;
510     NSString *appName;
511     
512     appName = @"Qemu";
513     appleMenu = [[NSMenu alloc] initWithTitle:@""];
514     
515     /* Add menu items */
516     title = [@"About " stringByAppendingString:appName];
517     [appleMenu addItemWithTitle:title action:@selector(orderFrontStandardAboutPanel:) keyEquivalent:@""];
519     [appleMenu addItem:[NSMenuItem separatorItem]];
521     title = [@"Hide " stringByAppendingString:appName];
522     [appleMenu addItemWithTitle:title action:@selector(hide:) keyEquivalent:@"h"];
524     menuItem = (NSMenuItem *)[appleMenu addItemWithTitle:@"Hide Others" action:@selector(hideOtherApplications:) keyEquivalent:@"h"];
525     [menuItem setKeyEquivalentModifierMask:(NSAlternateKeyMask|NSCommandKeyMask)];
527     [appleMenu addItemWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""];
529     [appleMenu addItem:[NSMenuItem separatorItem]];
531     title = [@"Quit " stringByAppendingString:appName];
532     [appleMenu addItemWithTitle:title action:@selector(terminate:) keyEquivalent:@"q"];
534     
535     /* Put menu into the menubar */
536     menuItem = [[NSMenuItem alloc] initWithTitle:@"" action:nil keyEquivalent:@""];
537     [menuItem setSubmenu:appleMenu];
538     [[NSApp mainMenu] addItem:menuItem];
540     /* Tell the application object that this is now the application menu */
541     [NSApp setAppleMenu:appleMenu];
543     /* Finally give up our references to the objects */
544     [appleMenu release];
545     [menuItem release];
548 /* Create a window menu */
549 static void setupWindowMenu(void)
551     NSMenu      *windowMenu;
552     NSMenuItem  *windowMenuItem;
553     NSMenuItem  *menuItem;
555     windowMenu = [[NSMenu alloc] initWithTitle:@"Window"];
556     
557     /* "Minimize" item */
558     menuItem = [[NSMenuItem alloc] initWithTitle:@"Minimize" action:@selector(performMiniaturize:) keyEquivalent:@"m"];
559     [windowMenu addItem:menuItem];
560     [menuItem release];
561     
562     /* Put menu into the menubar */
563     windowMenuItem = [[NSMenuItem alloc] initWithTitle:@"Window" action:nil keyEquivalent:@""];
564     [windowMenuItem setSubmenu:windowMenu];
565     [[NSApp mainMenu] addItem:windowMenuItem];
566     
567     /* Tell the application object that this is now the window menu */
568     [NSApp setWindowsMenu:windowMenu];
570     /* Finally give up our references to the objects */
571     [windowMenu release];
572     [windowMenuItem release];
576 static void CustomApplicationMain (argc, argv)
578     NSAutoreleasePool   *pool = [[NSAutoreleasePool alloc] init];
579     QemuCocoaGUIController *gui_controller;
580     CPSProcessSerNum PSN;
581     
582     [NSApplication sharedApplication];
583     
584     if (!CPSGetCurrentProcess(&PSN))
585         if (!CPSEnableForegroundOperation(&PSN,0x03,0x3C,0x2C,0x1103))
586             if (!CPSSetFrontProcess(&PSN))
587                 [NSApplication sharedApplication];
588                 
589     /* Set up the menubar */
590     [NSApp setMainMenu:[[NSMenu alloc] init]];
591     setApplicationMenu();
592     setupWindowMenu();
594     /* Create SDLMain and make it the app delegate */
595     gui_controller = [[QemuCocoaGUIController alloc] init];
596     [NSApp setDelegate:gui_controller];
597     
598     /* Start the main event loop */
599     [NSApp run];
600     
601     [gui_controller release];
602     [pool release];
605 /* Real main of qemu-cocoa */
606 int main(int argc, char **argv)
608     gArgc = argc;
609     gArgv = argv;
610     
611     CustomApplicationMain (argc, argv);
612     
613     return 0;