]> git.proxmox.com Git - qemu.git/blame - cocoa.m
fixed C0 status codes (Ralf Baechle)
[qemu.git] / cocoa.m
CommitLineData
5b0753e0
FB
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 */
25/*
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
36*/
da4dbf74 37
5b0753e0
FB
38#import <Cocoa/Cocoa.h>
39
da4dbf74
FB
40#include "vl.h"
41
5b0753e0
FB
42NSWindow *window = NULL;
43NSQuickDrawView *qd_view = NULL;
44
45
46int gArgc;
47char **gArgv;
48DisplayState current_ds;
49
50/* main defined in qemu/vl.c */
51int qemu_main(int argc, char **argv);
52
53/* To deal with miniaturization */
54@interface QemuWindow : NSWindow
55{ }
56@end
57
58
59/*
60 ------------------------------------------------------
61 Qemu Video Driver
62 ------------------------------------------------------
63*/
64
65/*
66 ------------------------------------------------------
67 cocoa_update
68 ------------------------------------------------------
69*/
70static void cocoa_update(DisplayState *ds, int x, int y, int w, int h)
71{
72 //printf("updating x=%d y=%d w=%d h=%d\n", x, y, w, h);
73
74 /* Use QDFlushPortBuffer() to flush content to display */
75 RgnHandle dirty = NewRgn ();
76 RgnHandle temp = NewRgn ();
77
78 SetEmptyRgn (dirty);
79
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);
89}
90
91/*
92 ------------------------------------------------------
93 cocoa_resize
94 ------------------------------------------------------
95*/
96static void cocoa_resize(DisplayState *ds, int w, int h)
97{
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;
166}
167
168/*
169 ------------------------------------------------------
170 keymap conversion
171 ------------------------------------------------------
172*/
173
174static int keymap[] =
175{
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 */
226};
227
228static int cocoa_keycode_to_qemu(int keycode)
229{
230 if(sizeof(keymap) <= keycode)
231 {
232 printf("(cocoa) warning unknow keycode 0x%x\n", keycode);
233 return 0;
234 }
235 return keymap[keycode];
236}
237
238/*
239 ------------------------------------------------------
240 cocoa_refresh
241 ------------------------------------------------------
242*/
243static void cocoa_refresh(DisplayState *ds)
244{
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]);
275
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);
301}
302
303/*
304 ------------------------------------------------------
305 cocoa_cleanup
306 ------------------------------------------------------
307*/
308
309static void cocoa_cleanup(void)
310{
311
312}
313
314/*
315 ------------------------------------------------------
316 cocoa_display_init
317 ------------------------------------------------------
318*/
319
320void cocoa_display_init(DisplayState *ds, int full_screen)
321{
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);
329}
330
331/*
332 ------------------------------------------------------
333 Interface with Cocoa
334 ------------------------------------------------------
335*/
336
337
338/*
339 ------------------------------------------------------
340 QemuWindow
341 Some trick from SDL to use miniwindow
342 ------------------------------------------------------
343*/
344static 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 }
359}
360
361@implementation QemuWindow
362- (void)miniaturize:(id)sender
363{
364
365 /* make the alpha channel opaque so anim won't have holes in it */
366 QZ_SetPortAlphaOpaque ();
367
368 [ super miniaturize:sender ];
369
370}
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 ];
392}
393
394@end
395
396
397/*
398 ------------------------------------------------------
399 QemuCocoaGUIController
400 NSApp's delegate - indeed main object
401 ------------------------------------------------------
402*/
403
404@interface QemuCocoaGUIController : NSObject
405{
406}
407- (void)applicationDidFinishLaunching: (NSNotification *) note;
408- (void)applicationWillTerminate:(NSNotification *)aNotification;
409
410- (void)openPanelDidEnd:(NSOpenPanel *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo;
411
412- (void)startEmulationWithArgc:(int)argc argv:(char**)argv;
413@end
414
415@implementation QemuCocoaGUIController
416/* Called when the internal event loop has just started running */
417- (void)applicationDidFinishLaunching: (NSNotification *) note
418{
5a246934
FB
419
420 /* Display an open dialog box if no argument were passed or
421 if qemu was launched from the finder ( the Finder passes "-psn" ) */
422
423 if( gArgc <= 1 || strncmp (gArgv[1], "-psn", 4) == 0)
5b0753e0
FB
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
7a674b13 433 [op beginSheetForDirectory:nil file:nil types:[NSArray arrayWithObjects:@"img",@"iso",@"dmg",@"qcow",@"cow",@"cloop",@"vmdk",nil]
5b0753e0
FB
434 modalForWindow:window modalDelegate:self
435 didEndSelector:@selector(openPanelDidEnd:returnCode:contextInfo:) contextInfo:NULL];
436 }
5a246934
FB
437 else
438 {
439 /* or Launch Qemu, with the global args */
440 [self startEmulationWithArgc:gArgc argv:gArgv];
441 }
5b0753e0
FB
442}
443
444- (void)applicationWillTerminate:(NSNotification *)aNotification
445{
446 printf("Application will terminate\n");
447 qemu_system_shutdown_request();
448 /* In order to avoid a crash */
449 exit(0);
450}
451
452- (void)openPanelDidEnd:(NSOpenPanel *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo
453{
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 }
474}
475
476- (void)startEmulationWithArgc:(int)argc argv:(char**)argv
477{
478 int status;
479 /* Launch Qemu */
480 printf("starting qemu...\n");
481 status = qemu_main (argc, argv);
482 exit(status);
483}
484@end
485
486/*
487 ------------------------------------------------------
488 Application Creation
489 ------------------------------------------------------
490*/
491
492/* Dock Connection */
493typedef struct CPSProcessSerNum
494{
495 UInt32 lo;
496 UInt32 hi;
497} CPSProcessSerNum;
498
499extern OSErr CPSGetCurrentProcess( CPSProcessSerNum *psn);
500extern OSErr CPSEnableForegroundOperation( CPSProcessSerNum *psn, UInt32 _arg2, UInt32 _arg3, UInt32 _arg4, UInt32 _arg5);
501extern OSErr CPSSetFrontProcess( CPSProcessSerNum *psn);
502
503/* Menu Creation */
504static void setApplicationMenu(void)
505{
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:@""];
518
519 [appleMenu addItem:[NSMenuItem separatorItem]];
520
521 title = [@"Hide " stringByAppendingString:appName];
522 [appleMenu addItemWithTitle:title action:@selector(hide:) keyEquivalent:@"h"];
523
524 menuItem = (NSMenuItem *)[appleMenu addItemWithTitle:@"Hide Others" action:@selector(hideOtherApplications:) keyEquivalent:@"h"];
525 [menuItem setKeyEquivalentModifierMask:(NSAlternateKeyMask|NSCommandKeyMask)];
526
527 [appleMenu addItemWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""];
528
529 [appleMenu addItem:[NSMenuItem separatorItem]];
530
531 title = [@"Quit " stringByAppendingString:appName];
532 [appleMenu addItemWithTitle:title action:@selector(terminate:) keyEquivalent:@"q"];
533
534
535 /* Put menu into the menubar */
536 menuItem = [[NSMenuItem alloc] initWithTitle:@"" action:nil keyEquivalent:@""];
537 [menuItem setSubmenu:appleMenu];
538 [[NSApp mainMenu] addItem:menuItem];
539
540 /* Tell the application object that this is now the application menu */
541 [NSApp setAppleMenu:appleMenu];
542
543 /* Finally give up our references to the objects */
544 [appleMenu release];
545 [menuItem release];
546}
547
548/* Create a window menu */
549static void setupWindowMenu(void)
550{
551 NSMenu *windowMenu;
552 NSMenuItem *windowMenuItem;
553 NSMenuItem *menuItem;
554
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];
569
570 /* Finally give up our references to the objects */
571 [windowMenu release];
572 [windowMenuItem release];
573
574}
575
576static void CustomApplicationMain (argc, argv)
577{
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();
593
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];
603}
604
605/* Real main of qemu-cocoa */
606int main(int argc, char **argv)
607{
608 gArgc = argc;
609 gArgv = argv;
610
611 CustomApplicationMain (argc, argv);
612
613 return 0;
614}