Statistics
| Branch: | Revision:

root / cocoa.m @ 5a246934

History | View | Annotate | Download (17 kB)

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
*/
37

    
38
#import <Cocoa/Cocoa.h>
39

    
40
#include "vl.h"
41

    
42
NSWindow *window = NULL;
43
NSQuickDrawView *qd_view = NULL;
44

    
45

    
46
int gArgc;
47
char **gArgv;
48
DisplayState current_ds;
49

    
50
/* main defined in qemu/vl.c */
51
int 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
*/
70
static 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
*/
96
static 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

    
174
static 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

    
228
static 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
*/
243
static 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

    
309
static void cocoa_cleanup(void) 
310
{
311

    
312
}
313

    
314
/*
315
 ------------------------------------------------------
316
    cocoa_display_init
317
 ------------------------------------------------------
318
*/
319

    
320
void 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
*/
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
    }
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
    /* 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)
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",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
    }
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 */
493
typedef struct CPSProcessSerNum
494
{
495
        UInt32                lo;
496
        UInt32                hi;
497
} CPSProcessSerNum;
498

    
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);
502

    
503
/* Menu Creation */
504
static 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 */
549
static 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

    
576
static 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 */
606
int main(int argc, char **argv)
607
{
608
    gArgc = argc;
609
    gArgv = argv;
610
    
611
    CustomApplicationMain (argc, argv);
612
    
613
    return 0;
614
}