]> git.proxmox.com Git - qemu.git/blame - cocoa.m
sparc update
[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{
419
420 /* Do whatever we want here : set up a pc list... */
421 {
422 NSOpenPanel *op = [[NSOpenPanel alloc] init];
423
424 cocoa_resize(&current_ds, 640, 400);
425
426 [op setPrompt:@"Boot image"];
427
428 [op setMessage:@"Select the disk image you want to boot.\n\nHit the \"Cancel\" button to quit"];
429
430 [op beginSheetForDirectory:nil file:nil types:[NSArray arrayWithObjects:@"img",@"iso",nil]
431 modalForWindow:window modalDelegate:self
432 didEndSelector:@selector(openPanelDidEnd:returnCode:contextInfo:) contextInfo:NULL];
433 }
434
435 /* or Launch Qemu, with the global args */
436 //[self startEmulationWithArgc:gArgc argv:gArgv];
437}
438
439- (void)applicationWillTerminate:(NSNotification *)aNotification
440{
441 printf("Application will terminate\n");
442 qemu_system_shutdown_request();
443 /* In order to avoid a crash */
444 exit(0);
445}
446
447- (void)openPanelDidEnd:(NSOpenPanel *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo
448{
449 if(returnCode == NSCancelButton)
450 {
451 exit(0);
452 }
453
454 if(returnCode == NSOKButton)
455 {
456 char *bin = "qemu";
457 char *img = (char*)[ [ sheet filename ] cString];
458
459 char **argv = (char**)malloc( sizeof(char*)*3 );
460
461 asprintf(&argv[0], "%s", bin);
462 asprintf(&argv[1], "-hda");
463 asprintf(&argv[2], "%s", img);
464
465 printf("Using argc %d argv %s -hda %s\n", 3, bin, img);
466
467 [self startEmulationWithArgc:3 argv:(char**)argv];
468 }
469}
470
471- (void)startEmulationWithArgc:(int)argc argv:(char**)argv
472{
473 int status;
474 /* Launch Qemu */
475 printf("starting qemu...\n");
476 status = qemu_main (argc, argv);
477 exit(status);
478}
479@end
480
481/*
482 ------------------------------------------------------
483 Application Creation
484 ------------------------------------------------------
485*/
486
487/* Dock Connection */
488typedef struct CPSProcessSerNum
489{
490 UInt32 lo;
491 UInt32 hi;
492} CPSProcessSerNum;
493
494extern OSErr CPSGetCurrentProcess( CPSProcessSerNum *psn);
495extern OSErr CPSEnableForegroundOperation( CPSProcessSerNum *psn, UInt32 _arg2, UInt32 _arg3, UInt32 _arg4, UInt32 _arg5);
496extern OSErr CPSSetFrontProcess( CPSProcessSerNum *psn);
497
498/* Menu Creation */
499static void setApplicationMenu(void)
500{
501 /* warning: this code is very odd */
502 NSMenu *appleMenu;
503 NSMenuItem *menuItem;
504 NSString *title;
505 NSString *appName;
506
507 appName = @"Qemu";
508 appleMenu = [[NSMenu alloc] initWithTitle:@""];
509
510 /* Add menu items */
511 title = [@"About " stringByAppendingString:appName];
512 [appleMenu addItemWithTitle:title action:@selector(orderFrontStandardAboutPanel:) keyEquivalent:@""];
513
514 [appleMenu addItem:[NSMenuItem separatorItem]];
515
516 title = [@"Hide " stringByAppendingString:appName];
517 [appleMenu addItemWithTitle:title action:@selector(hide:) keyEquivalent:@"h"];
518
519 menuItem = (NSMenuItem *)[appleMenu addItemWithTitle:@"Hide Others" action:@selector(hideOtherApplications:) keyEquivalent:@"h"];
520 [menuItem setKeyEquivalentModifierMask:(NSAlternateKeyMask|NSCommandKeyMask)];
521
522 [appleMenu addItemWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""];
523
524 [appleMenu addItem:[NSMenuItem separatorItem]];
525
526 title = [@"Quit " stringByAppendingString:appName];
527 [appleMenu addItemWithTitle:title action:@selector(terminate:) keyEquivalent:@"q"];
528
529
530 /* Put menu into the menubar */
531 menuItem = [[NSMenuItem alloc] initWithTitle:@"" action:nil keyEquivalent:@""];
532 [menuItem setSubmenu:appleMenu];
533 [[NSApp mainMenu] addItem:menuItem];
534
535 /* Tell the application object that this is now the application menu */
536 [NSApp setAppleMenu:appleMenu];
537
538 /* Finally give up our references to the objects */
539 [appleMenu release];
540 [menuItem release];
541}
542
543/* Create a window menu */
544static void setupWindowMenu(void)
545{
546 NSMenu *windowMenu;
547 NSMenuItem *windowMenuItem;
548 NSMenuItem *menuItem;
549
550 windowMenu = [[NSMenu alloc] initWithTitle:@"Window"];
551
552 /* "Minimize" item */
553 menuItem = [[NSMenuItem alloc] initWithTitle:@"Minimize" action:@selector(performMiniaturize:) keyEquivalent:@"m"];
554 [windowMenu addItem:menuItem];
555 [menuItem release];
556
557 /* Put menu into the menubar */
558 windowMenuItem = [[NSMenuItem alloc] initWithTitle:@"Window" action:nil keyEquivalent:@""];
559 [windowMenuItem setSubmenu:windowMenu];
560 [[NSApp mainMenu] addItem:windowMenuItem];
561
562 /* Tell the application object that this is now the window menu */
563 [NSApp setWindowsMenu:windowMenu];
564
565 /* Finally give up our references to the objects */
566 [windowMenu release];
567 [windowMenuItem release];
568
569}
570
571static void CustomApplicationMain (argc, argv)
572{
573 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
574 QemuCocoaGUIController *gui_controller;
575 CPSProcessSerNum PSN;
576
577 [NSApplication sharedApplication];
578
579 if (!CPSGetCurrentProcess(&PSN))
580 if (!CPSEnableForegroundOperation(&PSN,0x03,0x3C,0x2C,0x1103))
581 if (!CPSSetFrontProcess(&PSN))
582 [NSApplication sharedApplication];
583
584 /* Set up the menubar */
585 [NSApp setMainMenu:[[NSMenu alloc] init]];
586 setApplicationMenu();
587 setupWindowMenu();
588
589 /* Create SDLMain and make it the app delegate */
590 gui_controller = [[QemuCocoaGUIController alloc] init];
591 [NSApp setDelegate:gui_controller];
592
593 /* Start the main event loop */
594 [NSApp run];
595
596 [gui_controller release];
597 [pool release];
598}
599
600/* Real main of qemu-cocoa */
601int main(int argc, char **argv)
602{
603 gArgc = argc;
604 gArgv = argv;
605
606 CustomApplicationMain (argc, argv);
607
608 return 0;
609}