13e230dd2SCorentin Chary/* 23e230dd2SCorentin Chary * QEMU Cocoa CG display driver 33e230dd2SCorentin Chary * 43e230dd2SCorentin Chary * Copyright (c) 2008 Mike Kronenberg 53e230dd2SCorentin Chary * 63e230dd2SCorentin Chary * Permission is hereby granted, free of charge, to any person obtaining a copy 73e230dd2SCorentin Chary * of this software and associated documentation files (the "Software"), to deal 83e230dd2SCorentin Chary * in the Software without restriction, including without limitation the rights 93e230dd2SCorentin Chary * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 103e230dd2SCorentin Chary * copies of the Software, and to permit persons to whom the Software is 113e230dd2SCorentin Chary * furnished to do so, subject to the following conditions: 123e230dd2SCorentin Chary * 133e230dd2SCorentin Chary * The above copyright notice and this permission notice shall be included in 143e230dd2SCorentin Chary * all copies or substantial portions of the Software. 153e230dd2SCorentin Chary * 163e230dd2SCorentin Chary * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 173e230dd2SCorentin Chary * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 183e230dd2SCorentin Chary * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 193e230dd2SCorentin Chary * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 203e230dd2SCorentin Chary * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 213e230dd2SCorentin Chary * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 223e230dd2SCorentin Chary * THE SOFTWARE. 233e230dd2SCorentin Chary */ 243e230dd2SCorentin Chary 253e230dd2SCorentin Chary#import <Cocoa/Cocoa.h> 263bbbee18SAndreas Färber#include <crt_externs.h> 273e230dd2SCorentin Chary 283e230dd2SCorentin Chary#include "qemu-common.h" 2928ecbaeeSPaolo Bonzini#include "ui/console.h" 3021bae11aSGerd Hoffmann#include "ui/input.h" 319c17d615SPaolo Bonzini#include "sysemu/sysemu.h" 328524f1c7SJohn Arbuckle#include "qmp-commands.h" 33693a3e01SJohn Arbuckle#include "sysemu/blockdev.h" 343e230dd2SCorentin Chary 353e230dd2SCorentin Chary#ifndef MAC_OS_X_VERSION_10_5 363e230dd2SCorentin Chary#define MAC_OS_X_VERSION_10_5 1050 373e230dd2SCorentin Chary#endif 382ba9de6eSPeter Maydell#ifndef MAC_OS_X_VERSION_10_6 392ba9de6eSPeter Maydell#define MAC_OS_X_VERSION_10_6 1060 402ba9de6eSPeter Maydell#endif 4181801ae2SPeter Maydell#ifndef MAC_OS_X_VERSION_10_10 4281801ae2SPeter Maydell#define MAC_OS_X_VERSION_10_10 101000 4381801ae2SPeter Maydell#endif 443e230dd2SCorentin Chary 453e230dd2SCorentin Chary 463e230dd2SCorentin Chary//#define DEBUG 473e230dd2SCorentin Chary 483e230dd2SCorentin Chary#ifdef DEBUG 493e230dd2SCorentin Chary#define COCOA_DEBUG(...) { (void) fprintf (stdout, __VA_ARGS__); } 503e230dd2SCorentin Chary#else 513e230dd2SCorentin Chary#define COCOA_DEBUG(...) ((void) 0) 523e230dd2SCorentin Chary#endif 533e230dd2SCorentin Chary 543e230dd2SCorentin Chary#define cgrect(nsrect) (*(CGRect *)&(nsrect)) 553e230dd2SCorentin Chary 563e230dd2SCorentin Charytypedef struct { 573e230dd2SCorentin Chary int width; 583e230dd2SCorentin Chary int height; 593e230dd2SCorentin Chary int bitsPerComponent; 603e230dd2SCorentin Chary int bitsPerPixel; 613e230dd2SCorentin Chary} QEMUScreen; 623e230dd2SCorentin Chary 633e230dd2SCorentin CharyNSWindow *normalWindow; 643e230dd2SCorentin Charystatic DisplayChangeListener *dcl; 6521bae11aSGerd Hoffmannstatic int last_buttons; 663e230dd2SCorentin Chary 673e230dd2SCorentin Charyint gArgc; 683e230dd2SCorentin Charychar **gArgv; 695d1b2eefSProgrammingkidbool stretch_video; 708524f1c7SJohn ArbuckleNSTextField *pauseLabel; 71693a3e01SJohn ArbuckleNSArray * supportedImageFileTypes; 723e230dd2SCorentin Chary 733e230dd2SCorentin Chary// keymap conversion 743e230dd2SCorentin Charyint keymap[] = 753e230dd2SCorentin Chary{ 763e230dd2SCorentin Chary// SdlI macI macH SdlH 104xtH 104xtC sdl 773e230dd2SCorentin Chary 30, // 0 0x00 0x1e A QZ_a 783e230dd2SCorentin Chary 31, // 1 0x01 0x1f S QZ_s 793e230dd2SCorentin Chary 32, // 2 0x02 0x20 D QZ_d 803e230dd2SCorentin Chary 33, // 3 0x03 0x21 F QZ_f 813e230dd2SCorentin Chary 35, // 4 0x04 0x23 H QZ_h 823e230dd2SCorentin Chary 34, // 5 0x05 0x22 G QZ_g 833e230dd2SCorentin Chary 44, // 6 0x06 0x2c Z QZ_z 843e230dd2SCorentin Chary 45, // 7 0x07 0x2d X QZ_x 853e230dd2SCorentin Chary 46, // 8 0x08 0x2e C QZ_c 863e230dd2SCorentin Chary 47, // 9 0x09 0x2f V QZ_v 873e230dd2SCorentin Chary 0, // 10 0x0A Undefined 883e230dd2SCorentin Chary 48, // 11 0x0B 0x30 B QZ_b 893e230dd2SCorentin Chary 16, // 12 0x0C 0x10 Q QZ_q 903e230dd2SCorentin Chary 17, // 13 0x0D 0x11 W QZ_w 913e230dd2SCorentin Chary 18, // 14 0x0E 0x12 E QZ_e 923e230dd2SCorentin Chary 19, // 15 0x0F 0x13 R QZ_r 933e230dd2SCorentin Chary 21, // 16 0x10 0x15 Y QZ_y 943e230dd2SCorentin Chary 20, // 17 0x11 0x14 T QZ_t 953e230dd2SCorentin Chary 2, // 18 0x12 0x02 1 QZ_1 963e230dd2SCorentin Chary 3, // 19 0x13 0x03 2 QZ_2 973e230dd2SCorentin Chary 4, // 20 0x14 0x04 3 QZ_3 983e230dd2SCorentin Chary 5, // 21 0x15 0x05 4 QZ_4 993e230dd2SCorentin Chary 7, // 22 0x16 0x07 6 QZ_6 1003e230dd2SCorentin Chary 6, // 23 0x17 0x06 5 QZ_5 1013e230dd2SCorentin Chary 13, // 24 0x18 0x0d = QZ_EQUALS 1023e230dd2SCorentin Chary 10, // 25 0x19 0x0a 9 QZ_9 1033e230dd2SCorentin Chary 8, // 26 0x1A 0x08 7 QZ_7 1043e230dd2SCorentin Chary 12, // 27 0x1B 0x0c - QZ_MINUS 1053e230dd2SCorentin Chary 9, // 28 0x1C 0x09 8 QZ_8 1063e230dd2SCorentin Chary 11, // 29 0x1D 0x0b 0 QZ_0 1073e230dd2SCorentin Chary 27, // 30 0x1E 0x1b ] QZ_RIGHTBRACKET 1083e230dd2SCorentin Chary 24, // 31 0x1F 0x18 O QZ_o 1093e230dd2SCorentin Chary 22, // 32 0x20 0x16 U QZ_u 1103e230dd2SCorentin Chary 26, // 33 0x21 0x1a [ QZ_LEFTBRACKET 1113e230dd2SCorentin Chary 23, // 34 0x22 0x17 I QZ_i 1123e230dd2SCorentin Chary 25, // 35 0x23 0x19 P QZ_p 1133e230dd2SCorentin Chary 28, // 36 0x24 0x1c ENTER QZ_RETURN 1143e230dd2SCorentin Chary 38, // 37 0x25 0x26 L QZ_l 1153e230dd2SCorentin Chary 36, // 38 0x26 0x24 J QZ_j 1163e230dd2SCorentin Chary 40, // 39 0x27 0x28 ' QZ_QUOTE 1173e230dd2SCorentin Chary 37, // 40 0x28 0x25 K QZ_k 1183e230dd2SCorentin Chary 39, // 41 0x29 0x27 ; QZ_SEMICOLON 1193e230dd2SCorentin Chary 43, // 42 0x2A 0x2b \ QZ_BACKSLASH 1203e230dd2SCorentin Chary 51, // 43 0x2B 0x33 , QZ_COMMA 1213e230dd2SCorentin Chary 53, // 44 0x2C 0x35 / QZ_SLASH 1223e230dd2SCorentin Chary 49, // 45 0x2D 0x31 N QZ_n 1233e230dd2SCorentin Chary 50, // 46 0x2E 0x32 M QZ_m 1243e230dd2SCorentin Chary 52, // 47 0x2F 0x34 . QZ_PERIOD 1253e230dd2SCorentin Chary 15, // 48 0x30 0x0f TAB QZ_TAB 1263e230dd2SCorentin Chary 57, // 49 0x31 0x39 SPACE QZ_SPACE 1273e230dd2SCorentin Chary 41, // 50 0x32 0x29 ` QZ_BACKQUOTE 1283e230dd2SCorentin Chary 14, // 51 0x33 0x0e BKSP QZ_BACKSPACE 1293e230dd2SCorentin Chary 0, // 52 0x34 Undefined 1303e230dd2SCorentin Chary 1, // 53 0x35 0x01 ESC QZ_ESCAPE 1318895919aSPeter Maydell 220, // 54 0x36 0xdc E0,5C R GUI QZ_RMETA 1328895919aSPeter Maydell 219, // 55 0x37 0xdb E0,5B L GUI QZ_LMETA 1333e230dd2SCorentin Chary 42, // 56 0x38 0x2a L SHFT QZ_LSHIFT 1343e230dd2SCorentin Chary 58, // 57 0x39 0x3a CAPS QZ_CAPSLOCK 1353e230dd2SCorentin Chary 56, // 58 0x3A 0x38 L ALT QZ_LALT 1363e230dd2SCorentin Chary 29, // 59 0x3B 0x1d L CTRL QZ_LCTRL 1373e230dd2SCorentin Chary 54, // 60 0x3C 0x36 R SHFT QZ_RSHIFT 1383e230dd2SCorentin Chary 184,// 61 0x3D 0xb8 E0,38 R ALT QZ_RALT 1393e230dd2SCorentin Chary 157,// 62 0x3E 0x9d E0,1D R CTRL QZ_RCTRL 1403e230dd2SCorentin Chary 0, // 63 0x3F Undefined 1413e230dd2SCorentin Chary 0, // 64 0x40 Undefined 1423e230dd2SCorentin Chary 0, // 65 0x41 Undefined 1433e230dd2SCorentin Chary 0, // 66 0x42 Undefined 1443e230dd2SCorentin Chary 55, // 67 0x43 0x37 KP * QZ_KP_MULTIPLY 1453e230dd2SCorentin Chary 0, // 68 0x44 Undefined 1463e230dd2SCorentin Chary 78, // 69 0x45 0x4e KP + QZ_KP_PLUS 1473e230dd2SCorentin Chary 0, // 70 0x46 Undefined 1483e230dd2SCorentin Chary 69, // 71 0x47 0x45 NUM QZ_NUMLOCK 1493e230dd2SCorentin Chary 0, // 72 0x48 Undefined 1503e230dd2SCorentin Chary 0, // 73 0x49 Undefined 1513e230dd2SCorentin Chary 0, // 74 0x4A Undefined 1523e230dd2SCorentin Chary 181,// 75 0x4B 0xb5 E0,35 KP / QZ_KP_DIVIDE 1533e230dd2SCorentin Chary 152,// 76 0x4C 0x9c E0,1C KP EN QZ_KP_ENTER 1543e230dd2SCorentin Chary 0, // 77 0x4D undefined 1553e230dd2SCorentin Chary 74, // 78 0x4E 0x4a KP - QZ_KP_MINUS 1563e230dd2SCorentin Chary 0, // 79 0x4F Undefined 1573e230dd2SCorentin Chary 0, // 80 0x50 Undefined 1583e230dd2SCorentin Chary 0, // 81 0x51 QZ_KP_EQUALS 1593e230dd2SCorentin Chary 82, // 82 0x52 0x52 KP 0 QZ_KP0 1603e230dd2SCorentin Chary 79, // 83 0x53 0x4f KP 1 QZ_KP1 1613e230dd2SCorentin Chary 80, // 84 0x54 0x50 KP 2 QZ_KP2 1623e230dd2SCorentin Chary 81, // 85 0x55 0x51 KP 3 QZ_KP3 1633e230dd2SCorentin Chary 75, // 86 0x56 0x4b KP 4 QZ_KP4 1643e230dd2SCorentin Chary 76, // 87 0x57 0x4c KP 5 QZ_KP5 1653e230dd2SCorentin Chary 77, // 88 0x58 0x4d KP 6 QZ_KP6 1663e230dd2SCorentin Chary 71, // 89 0x59 0x47 KP 7 QZ_KP7 1673e230dd2SCorentin Chary 0, // 90 0x5A Undefined 1683e230dd2SCorentin Chary 72, // 91 0x5B 0x48 KP 8 QZ_KP8 1693e230dd2SCorentin Chary 73, // 92 0x5C 0x49 KP 9 QZ_KP9 1703e230dd2SCorentin Chary 0, // 93 0x5D Undefined 1713e230dd2SCorentin Chary 0, // 94 0x5E Undefined 1723e230dd2SCorentin Chary 0, // 95 0x5F Undefined 1733e230dd2SCorentin Chary 63, // 96 0x60 0x3f F5 QZ_F5 1743e230dd2SCorentin Chary 64, // 97 0x61 0x40 F6 QZ_F6 1753e230dd2SCorentin Chary 65, // 98 0x62 0x41 F7 QZ_F7 1763e230dd2SCorentin Chary 61, // 99 0x63 0x3d F3 QZ_F3 1773e230dd2SCorentin Chary 66, // 100 0x64 0x42 F8 QZ_F8 1783e230dd2SCorentin Chary 67, // 101 0x65 0x43 F9 QZ_F9 1793e230dd2SCorentin Chary 0, // 102 0x66 Undefined 1803e230dd2SCorentin Chary 87, // 103 0x67 0x57 F11 QZ_F11 1813e230dd2SCorentin Chary 0, // 104 0x68 Undefined 1823e230dd2SCorentin Chary 183,// 105 0x69 0xb7 QZ_PRINT 1833e230dd2SCorentin Chary 0, // 106 0x6A Undefined 1843e230dd2SCorentin Chary 70, // 107 0x6B 0x46 SCROLL QZ_SCROLLOCK 1853e230dd2SCorentin Chary 0, // 108 0x6C Undefined 1863e230dd2SCorentin Chary 68, // 109 0x6D 0x44 F10 QZ_F10 1873e230dd2SCorentin Chary 0, // 110 0x6E Undefined 1883e230dd2SCorentin Chary 88, // 111 0x6F 0x58 F12 QZ_F12 1893e230dd2SCorentin Chary 0, // 112 0x70 Undefined 1903e230dd2SCorentin Chary 110,// 113 0x71 0x0 QZ_PAUSE 1913e230dd2SCorentin Chary 210,// 114 0x72 0xd2 E0,52 INSERT QZ_INSERT 1923e230dd2SCorentin Chary 199,// 115 0x73 0xc7 E0,47 HOME QZ_HOME 1933e230dd2SCorentin Chary 201,// 116 0x74 0xc9 E0,49 PG UP QZ_PAGEUP 1943e230dd2SCorentin Chary 211,// 117 0x75 0xd3 E0,53 DELETE QZ_DELETE 1953e230dd2SCorentin Chary 62, // 118 0x76 0x3e F4 QZ_F4 1963e230dd2SCorentin Chary 207,// 119 0x77 0xcf E0,4f END QZ_END 1973e230dd2SCorentin Chary 60, // 120 0x78 0x3c F2 QZ_F2 1983e230dd2SCorentin Chary 209,// 121 0x79 0xd1 E0,51 PG DN QZ_PAGEDOWN 1993e230dd2SCorentin Chary 59, // 122 0x7A 0x3b F1 QZ_F1 2003e230dd2SCorentin Chary 203,// 123 0x7B 0xcb e0,4B L ARROW QZ_LEFT 2013e230dd2SCorentin Chary 205,// 124 0x7C 0xcd e0,4D R ARROW QZ_RIGHT 2023e230dd2SCorentin Chary 208,// 125 0x7D 0xd0 E0,50 D ARROW QZ_DOWN 2033e230dd2SCorentin Chary 200,// 126 0x7E 0xc8 E0,48 U ARROW QZ_UP 2043e230dd2SCorentin Chary/* completed according to http://www.libsdl.org/cgi/cvsweb.cgi/SDL12/src/video/quartz/SDL_QuartzKeys.h?rev=1.6&content-type=text/x-cvsweb-markup */ 2053e230dd2SCorentin Chary 20649b9bd4dSPeter Maydell/* Additional 104 Key XP-Keyboard Scancodes from http://www.computer-engineering.org/ps2keyboard/scancodes1.html */ 2073e230dd2SCorentin Chary/* 2083e230dd2SCorentin Chary 221 // 0xdd e0,5d APPS 2093e230dd2SCorentin Chary // E0,2A,E0,37 PRNT SCRN 2103e230dd2SCorentin Chary // E1,1D,45,E1,9D,C5 PAUSE 2113e230dd2SCorentin Chary 83 // 0x53 0x53 KP . 2123e230dd2SCorentin Chary// ACPI Scan Codes 2133e230dd2SCorentin Chary 222 // 0xde E0, 5E Power 2143e230dd2SCorentin Chary 223 // 0xdf E0, 5F Sleep 2153e230dd2SCorentin Chary 227 // 0xe3 E0, 63 Wake 2163e230dd2SCorentin Chary// Windows Multimedia Scan Codes 2173e230dd2SCorentin Chary 153 // 0x99 E0, 19 Next Track 2183e230dd2SCorentin Chary 144 // 0x90 E0, 10 Previous Track 2193e230dd2SCorentin Chary 164 // 0xa4 E0, 24 Stop 2203e230dd2SCorentin Chary 162 // 0xa2 E0, 22 Play/Pause 2213e230dd2SCorentin Chary 160 // 0xa0 E0, 20 Mute 2223e230dd2SCorentin Chary 176 // 0xb0 E0, 30 Volume Up 2233e230dd2SCorentin Chary 174 // 0xae E0, 2E Volume Down 2243e230dd2SCorentin Chary 237 // 0xed E0, 6D Media Select 2253e230dd2SCorentin Chary 236 // 0xec E0, 6C E-Mail 2263e230dd2SCorentin Chary 161 // 0xa1 E0, 21 Calculator 2273e230dd2SCorentin Chary 235 // 0xeb E0, 6B My Computer 2283e230dd2SCorentin Chary 229 // 0xe5 E0, 65 WWW Search 2293e230dd2SCorentin Chary 178 // 0xb2 E0, 32 WWW Home 2303e230dd2SCorentin Chary 234 // 0xea E0, 6A WWW Back 2313e230dd2SCorentin Chary 233 // 0xe9 E0, 69 WWW Forward 2323e230dd2SCorentin Chary 232 // 0xe8 E0, 68 WWW Stop 2333e230dd2SCorentin Chary 231 // 0xe7 E0, 67 WWW Refresh 2343e230dd2SCorentin Chary 230 // 0xe6 E0, 66 WWW Favorites 2353e230dd2SCorentin Chary*/ 2363e230dd2SCorentin Chary}; 2373e230dd2SCorentin Chary 2383e230dd2SCorentin Charystatic int cocoa_keycode_to_qemu(int keycode) 2393e230dd2SCorentin Chary{ 2405d70192bSStefan Weil if (ARRAY_SIZE(keymap) <= keycode) { 24101cc4e6fSPeter Maydell fprintf(stderr, "(cocoa) warning unknown keycode 0x%x\n", keycode); 2423e230dd2SCorentin Chary return 0; 2433e230dd2SCorentin Chary } 2443e230dd2SCorentin Chary return keymap[keycode]; 2453e230dd2SCorentin Chary} 2463e230dd2SCorentin Chary 247693a3e01SJohn Arbuckle/* Displays an alert dialog box with the specified message */ 248693a3e01SJohn Arbucklestatic void QEMU_Alert(NSString *message) 249693a3e01SJohn Arbuckle{ 250693a3e01SJohn Arbuckle NSAlert *alert; 251693a3e01SJohn Arbuckle alert = [NSAlert new]; 252693a3e01SJohn Arbuckle [alert setMessageText: message]; 253693a3e01SJohn Arbuckle [alert runModal]; 254693a3e01SJohn Arbuckle} 2553e230dd2SCorentin Chary 256693a3e01SJohn Arbuckle/* Handles any errors that happen with a device transaction */ 257693a3e01SJohn Arbucklestatic void handleAnyDeviceErrors(Error * err) 258693a3e01SJohn Arbuckle{ 259693a3e01SJohn Arbuckle if (err) { 260693a3e01SJohn Arbuckle QEMU_Alert([NSString stringWithCString: error_get_pretty(err) 261693a3e01SJohn Arbuckle encoding: NSASCIIStringEncoding]); 262693a3e01SJohn Arbuckle error_free(err); 263693a3e01SJohn Arbuckle } 264693a3e01SJohn Arbuckle} 2653e230dd2SCorentin Chary 2663e230dd2SCorentin Chary/* 2673e230dd2SCorentin Chary ------------------------------------------------------ 2683e230dd2SCorentin Chary QemuCocoaView 2693e230dd2SCorentin Chary ------------------------------------------------------ 2703e230dd2SCorentin Chary*/ 2713e230dd2SCorentin Chary@interface QemuCocoaView : NSView 2723e230dd2SCorentin Chary{ 2733e230dd2SCorentin Chary QEMUScreen screen; 2743e230dd2SCorentin Chary NSWindow *fullScreenWindow; 2753e230dd2SCorentin Chary float cx,cy,cw,ch,cdx,cdy; 2763e230dd2SCorentin Chary CGDataProviderRef dataProviderRef; 2773e230dd2SCorentin Chary int modifiers_state[256]; 27849b9bd4dSPeter Maydell BOOL isMouseGrabbed; 2793e230dd2SCorentin Chary BOOL isFullscreen; 2803e230dd2SCorentin Chary BOOL isAbsoluteEnabled; 281f61c387eSPeter Maydell BOOL isMouseDeassociated; 2823e230dd2SCorentin Chary} 2835e00d3acSGerd Hoffmann- (void) switchSurface:(DisplaySurface *)surface; 2843e230dd2SCorentin Chary- (void) grabMouse; 2853e230dd2SCorentin Chary- (void) ungrabMouse; 2863e230dd2SCorentin Chary- (void) toggleFullScreen:(id)sender; 2873e230dd2SCorentin Chary- (void) handleEvent:(NSEvent *)event; 2883e230dd2SCorentin Chary- (void) setAbsoluteEnabled:(BOOL)tIsAbsoluteEnabled; 289f61c387eSPeter Maydell/* The state surrounding mouse grabbing is potentially confusing. 290f61c387eSPeter Maydell * isAbsoluteEnabled tracks qemu_input_is_absolute() [ie "is the emulated 291f61c387eSPeter Maydell * pointing device an absolute-position one?"], but is only updated on 292f61c387eSPeter Maydell * next refresh. 293f61c387eSPeter Maydell * isMouseGrabbed tracks whether GUI events are directed to the guest; 294f61c387eSPeter Maydell * it controls whether special keys like Cmd get sent to the guest, 295f61c387eSPeter Maydell * and whether we capture the mouse when in non-absolute mode. 296f61c387eSPeter Maydell * isMouseDeassociated tracks whether we've told MacOSX to disassociate 297f61c387eSPeter Maydell * the mouse and mouse cursor position by calling 298f61c387eSPeter Maydell * CGAssociateMouseAndMouseCursorPosition(FALSE) 299f61c387eSPeter Maydell * (which basically happens if we grab in non-absolute mode). 300f61c387eSPeter Maydell */ 30149b9bd4dSPeter Maydell- (BOOL) isMouseGrabbed; 3023e230dd2SCorentin Chary- (BOOL) isAbsoluteEnabled; 303f61c387eSPeter Maydell- (BOOL) isMouseDeassociated; 3043e230dd2SCorentin Chary- (float) cdx; 3053e230dd2SCorentin Chary- (float) cdy; 3063e230dd2SCorentin Chary- (QEMUScreen) gscreen; 307*3b178b71SJohn Arbuckle- (void) raiseAllKeys; 3083e230dd2SCorentin Chary@end 3093e230dd2SCorentin Chary 3107fee199cSAndreas FärberQemuCocoaView *cocoaView; 3117fee199cSAndreas Färber 3123e230dd2SCorentin Chary@implementation QemuCocoaView 3133e230dd2SCorentin Chary- (id)initWithFrame:(NSRect)frameRect 3143e230dd2SCorentin Chary{ 3153e230dd2SCorentin Chary COCOA_DEBUG("QemuCocoaView: initWithFrame\n"); 3163e230dd2SCorentin Chary 3173e230dd2SCorentin Chary self = [super initWithFrame:frameRect]; 3183e230dd2SCorentin Chary if (self) { 3193e230dd2SCorentin Chary 3203e230dd2SCorentin Chary screen.bitsPerComponent = 8; 3213e230dd2SCorentin Chary screen.bitsPerPixel = 32; 3223e230dd2SCorentin Chary screen.width = frameRect.size.width; 3233e230dd2SCorentin Chary screen.height = frameRect.size.height; 3243e230dd2SCorentin Chary 3253e230dd2SCorentin Chary } 3263e230dd2SCorentin Chary return self; 3273e230dd2SCorentin Chary} 3283e230dd2SCorentin Chary 3293e230dd2SCorentin Chary- (void) dealloc 3303e230dd2SCorentin Chary{ 3313e230dd2SCorentin Chary COCOA_DEBUG("QemuCocoaView: dealloc\n"); 3323e230dd2SCorentin Chary 3333e230dd2SCorentin Chary if (dataProviderRef) 3343e230dd2SCorentin Chary CGDataProviderRelease(dataProviderRef); 3353e230dd2SCorentin Chary 3363e230dd2SCorentin Chary [super dealloc]; 3373e230dd2SCorentin Chary} 3383e230dd2SCorentin Chary 3393e230dd2SCorentin Chary- (BOOL) isOpaque 3403e230dd2SCorentin Chary{ 3413e230dd2SCorentin Chary return YES; 3423e230dd2SCorentin Chary} 3433e230dd2SCorentin Chary 3445dd45beeSPeter Maydell- (BOOL) screenContainsPoint:(NSPoint) p 3455dd45beeSPeter Maydell{ 3465dd45beeSPeter Maydell return (p.x > -1 && p.x < screen.width && p.y > -1 && p.y < screen.height); 3475dd45beeSPeter Maydell} 3485dd45beeSPeter Maydell 34913aefd30SPeter Maydell- (void) hideCursor 35013aefd30SPeter Maydell{ 35113aefd30SPeter Maydell if (!cursor_hide) { 35213aefd30SPeter Maydell return; 35313aefd30SPeter Maydell } 35413aefd30SPeter Maydell [NSCursor hide]; 35513aefd30SPeter Maydell} 35613aefd30SPeter Maydell 35713aefd30SPeter Maydell- (void) unhideCursor 35813aefd30SPeter Maydell{ 35913aefd30SPeter Maydell if (!cursor_hide) { 36013aefd30SPeter Maydell return; 36113aefd30SPeter Maydell } 36213aefd30SPeter Maydell [NSCursor unhide]; 36313aefd30SPeter Maydell} 36413aefd30SPeter Maydell 3653e230dd2SCorentin Chary- (void) drawRect:(NSRect) rect 3663e230dd2SCorentin Chary{ 3673e230dd2SCorentin Chary COCOA_DEBUG("QemuCocoaView: drawRect\n"); 3683e230dd2SCorentin Chary 3693e230dd2SCorentin Chary // get CoreGraphic context 3703e230dd2SCorentin Chary CGContextRef viewContextRef = [[NSGraphicsContext currentContext] graphicsPort]; 3713e230dd2SCorentin Chary CGContextSetInterpolationQuality (viewContextRef, kCGInterpolationNone); 3723e230dd2SCorentin Chary CGContextSetShouldAntialias (viewContextRef, NO); 3733e230dd2SCorentin Chary 3743e230dd2SCorentin Chary // draw screen bitmap directly to Core Graphics context 3757d270b1cSPeter Maydell if (!dataProviderRef) { 3767d270b1cSPeter Maydell // Draw request before any guest device has set up a framebuffer: 3777d270b1cSPeter Maydell // just draw an opaque black rectangle 3787d270b1cSPeter Maydell CGContextSetRGBFillColor(viewContextRef, 0, 0, 0, 1.0); 3797d270b1cSPeter Maydell CGContextFillRect(viewContextRef, NSRectToCGRect(rect)); 3807d270b1cSPeter Maydell } else { 3813e230dd2SCorentin Chary CGImageRef imageRef = CGImageCreate( 3823e230dd2SCorentin Chary screen.width, //width 3833e230dd2SCorentin Chary screen.height, //height 3843e230dd2SCorentin Chary screen.bitsPerComponent, //bitsPerComponent 3853e230dd2SCorentin Chary screen.bitsPerPixel, //bitsPerPixel 3863e230dd2SCorentin Chary (screen.width * (screen.bitsPerComponent/2)), //bytesPerRow 3873e230dd2SCorentin Chary#ifdef __LITTLE_ENDIAN__ 3883e230dd2SCorentin Chary CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB), //colorspace for OS X >= 10.4 3893e230dd2SCorentin Chary kCGBitmapByteOrder32Little | kCGImageAlphaNoneSkipFirst, 3903e230dd2SCorentin Chary#else 3913e230dd2SCorentin Chary CGColorSpaceCreateDeviceRGB(), //colorspace for OS X < 10.4 (actually ppc) 3923e230dd2SCorentin Chary kCGImageAlphaNoneSkipFirst, //bitmapInfo 3933e230dd2SCorentin Chary#endif 3943e230dd2SCorentin Chary dataProviderRef, //provider 3953e230dd2SCorentin Chary NULL, //decode 3963e230dd2SCorentin Chary 0, //interpolate 3973e230dd2SCorentin Chary kCGRenderingIntentDefault //intent 3983e230dd2SCorentin Chary ); 3993e230dd2SCorentin Chary // selective drawing code (draws only dirty rectangles) (OS X >= 10.4) 4003e230dd2SCorentin Chary const NSRect *rectList; 4013e230dd2SCorentin Chary NSInteger rectCount; 4023e230dd2SCorentin Chary int i; 4033e230dd2SCorentin Chary CGImageRef clipImageRef; 4043e230dd2SCorentin Chary CGRect clipRect; 4053e230dd2SCorentin Chary 4063e230dd2SCorentin Chary [self getRectsBeingDrawn:&rectList count:&rectCount]; 4073e230dd2SCorentin Chary for (i = 0; i < rectCount; i++) { 4083e230dd2SCorentin Chary clipRect.origin.x = rectList[i].origin.x / cdx; 4093e230dd2SCorentin Chary clipRect.origin.y = (float)screen.height - (rectList[i].origin.y + rectList[i].size.height) / cdy; 4103e230dd2SCorentin Chary clipRect.size.width = rectList[i].size.width / cdx; 4113e230dd2SCorentin Chary clipRect.size.height = rectList[i].size.height / cdy; 4123e230dd2SCorentin Chary clipImageRef = CGImageCreateWithImageInRect( 4133e230dd2SCorentin Chary imageRef, 4143e230dd2SCorentin Chary clipRect 4153e230dd2SCorentin Chary ); 4163e230dd2SCorentin Chary CGContextDrawImage (viewContextRef, cgrect(rectList[i]), clipImageRef); 4173e230dd2SCorentin Chary CGImageRelease (clipImageRef); 4183e230dd2SCorentin Chary } 4193e230dd2SCorentin Chary CGImageRelease (imageRef); 4203e230dd2SCorentin Chary } 4213e230dd2SCorentin Chary} 4223e230dd2SCorentin Chary 4233e230dd2SCorentin Chary- (void) setContentDimensions 4243e230dd2SCorentin Chary{ 4253e230dd2SCorentin Chary COCOA_DEBUG("QemuCocoaView: setContentDimensions\n"); 4263e230dd2SCorentin Chary 4273e230dd2SCorentin Chary if (isFullscreen) { 4283e230dd2SCorentin Chary cdx = [[NSScreen mainScreen] frame].size.width / (float)screen.width; 4293e230dd2SCorentin Chary cdy = [[NSScreen mainScreen] frame].size.height / (float)screen.height; 4305d1b2eefSProgrammingkid 4315d1b2eefSProgrammingkid /* stretches video, but keeps same aspect ratio */ 4325d1b2eefSProgrammingkid if (stretch_video == true) { 4335d1b2eefSProgrammingkid /* use smallest stretch value - prevents clipping on sides */ 4345d1b2eefSProgrammingkid if (MIN(cdx, cdy) == cdx) { 4355d1b2eefSProgrammingkid cdy = cdx; 4365d1b2eefSProgrammingkid } else { 4375d1b2eefSProgrammingkid cdx = cdy; 4385d1b2eefSProgrammingkid } 4395d1b2eefSProgrammingkid } else { /* No stretching */ 4405d1b2eefSProgrammingkid cdx = cdy = 1; 4415d1b2eefSProgrammingkid } 4423e230dd2SCorentin Chary cw = screen.width * cdx; 4433e230dd2SCorentin Chary ch = screen.height * cdy; 4443e230dd2SCorentin Chary cx = ([[NSScreen mainScreen] frame].size.width - cw) / 2.0; 4453e230dd2SCorentin Chary cy = ([[NSScreen mainScreen] frame].size.height - ch) / 2.0; 4463e230dd2SCorentin Chary } else { 4473e230dd2SCorentin Chary cx = 0; 4483e230dd2SCorentin Chary cy = 0; 4493e230dd2SCorentin Chary cw = screen.width; 4503e230dd2SCorentin Chary ch = screen.height; 4513e230dd2SCorentin Chary cdx = 1.0; 4523e230dd2SCorentin Chary cdy = 1.0; 4533e230dd2SCorentin Chary } 4543e230dd2SCorentin Chary} 4553e230dd2SCorentin Chary 4565e00d3acSGerd Hoffmann- (void) switchSurface:(DisplaySurface *)surface 4573e230dd2SCorentin Chary{ 4585e00d3acSGerd Hoffmann COCOA_DEBUG("QemuCocoaView: switchSurface\n"); 4593e230dd2SCorentin Chary 4608510d91eSPeter Maydell int w = surface_width(surface); 4618510d91eSPeter Maydell int h = surface_height(surface); 462381600daSPeter Maydell /* cdx == 0 means this is our very first surface, in which case we need 463381600daSPeter Maydell * to recalculate the content dimensions even if it happens to be the size 464381600daSPeter Maydell * of the initial empty window. 465381600daSPeter Maydell */ 466381600daSPeter Maydell bool isResize = (w != screen.width || h != screen.height || cdx == 0.0); 467d3345a04SPeter Maydell 468d3345a04SPeter Maydell int oldh = screen.height; 469d3345a04SPeter Maydell if (isResize) { 470d3345a04SPeter Maydell // Resize before we trigger the redraw, or we'll redraw at the wrong size 471d3345a04SPeter Maydell COCOA_DEBUG("switchSurface: new size %d x %d\n", w, h); 472d3345a04SPeter Maydell screen.width = w; 473d3345a04SPeter Maydell screen.height = h; 474d3345a04SPeter Maydell [self setContentDimensions]; 475d3345a04SPeter Maydell [self setFrame:NSMakeRect(cx, cy, cw, ch)]; 476d3345a04SPeter Maydell } 4778510d91eSPeter Maydell 4783e230dd2SCorentin Chary // update screenBuffer 4793e230dd2SCorentin Chary if (dataProviderRef) 4803e230dd2SCorentin Chary CGDataProviderRelease(dataProviderRef); 4813e230dd2SCorentin Chary 4823e230dd2SCorentin Chary //sync host window color space with guests 4835e00d3acSGerd Hoffmann screen.bitsPerPixel = surface_bits_per_pixel(surface); 4845e00d3acSGerd Hoffmann screen.bitsPerComponent = surface_bytes_per_pixel(surface) * 2; 4853e230dd2SCorentin Chary 4865e00d3acSGerd Hoffmann dataProviderRef = CGDataProviderCreateWithData(NULL, surface_data(surface), w * 4 * h, NULL); 4873e230dd2SCorentin Chary 4883e230dd2SCorentin Chary // update windows 4893e230dd2SCorentin Chary if (isFullscreen) { 4903e230dd2SCorentin Chary [[fullScreenWindow contentView] setFrame:[[NSScreen mainScreen] frame]]; 491d3345a04SPeter Maydell [normalWindow setFrame:NSMakeRect([normalWindow frame].origin.x, [normalWindow frame].origin.y - h + oldh, w, h + [normalWindow frame].size.height - oldh) display:NO animate:NO]; 4923e230dd2SCorentin Chary } else { 4933e230dd2SCorentin Chary if (qemu_name) 4943e230dd2SCorentin Chary [normalWindow setTitle:[NSString stringWithFormat:@"QEMU %s", qemu_name]]; 495d3345a04SPeter Maydell [normalWindow setFrame:NSMakeRect([normalWindow frame].origin.x, [normalWindow frame].origin.y - h + oldh, w, h + [normalWindow frame].size.height - oldh) display:YES animate:NO]; 4963e230dd2SCorentin Chary } 497d3345a04SPeter Maydell 498d3345a04SPeter Maydell if (isResize) { 4993e230dd2SCorentin Chary [normalWindow center]; 500d3345a04SPeter Maydell } 5013e230dd2SCorentin Chary} 5023e230dd2SCorentin Chary 5033e230dd2SCorentin Chary- (void) toggleFullScreen:(id)sender 5043e230dd2SCorentin Chary{ 5053e230dd2SCorentin Chary COCOA_DEBUG("QemuCocoaView: toggleFullScreen\n"); 5063e230dd2SCorentin Chary 5073e230dd2SCorentin Chary if (isFullscreen) { // switch from fullscreen to desktop 5083e230dd2SCorentin Chary isFullscreen = FALSE; 5093e230dd2SCorentin Chary [self ungrabMouse]; 5103e230dd2SCorentin Chary [self setContentDimensions]; 5113e230dd2SCorentin Chary if ([NSView respondsToSelector:@selector(exitFullScreenModeWithOptions:)]) { // test if "exitFullScreenModeWithOptions" is supported on host at runtime 5123e230dd2SCorentin Chary [self exitFullScreenModeWithOptions:nil]; 5133e230dd2SCorentin Chary } else { 5143e230dd2SCorentin Chary [fullScreenWindow close]; 5153e230dd2SCorentin Chary [normalWindow setContentView: self]; 5163e230dd2SCorentin Chary [normalWindow makeKeyAndOrderFront: self]; 5173e230dd2SCorentin Chary [NSMenu setMenuBarVisible:YES]; 5183e230dd2SCorentin Chary } 5193e230dd2SCorentin Chary } else { // switch from desktop to fullscreen 5203e230dd2SCorentin Chary isFullscreen = TRUE; 5215d1b2eefSProgrammingkid [normalWindow orderOut: nil]; /* Hide the window */ 5223e230dd2SCorentin Chary [self grabMouse]; 5233e230dd2SCorentin Chary [self setContentDimensions]; 5243e230dd2SCorentin Chary if ([NSView respondsToSelector:@selector(enterFullScreenMode:withOptions:)]) { // test if "enterFullScreenMode:withOptions" is supported on host at runtime 5253e230dd2SCorentin Chary [self enterFullScreenMode:[NSScreen mainScreen] withOptions:[NSDictionary dictionaryWithObjectsAndKeys: 5263e230dd2SCorentin Chary [NSNumber numberWithBool:NO], NSFullScreenModeAllScreens, 5273e230dd2SCorentin Chary [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:NO], kCGDisplayModeIsStretched, nil], NSFullScreenModeSetting, 5283e230dd2SCorentin Chary nil]]; 5293e230dd2SCorentin Chary } else { 5303e230dd2SCorentin Chary [NSMenu setMenuBarVisible:NO]; 5313e230dd2SCorentin Chary fullScreenWindow = [[NSWindow alloc] initWithContentRect:[[NSScreen mainScreen] frame] 5323e230dd2SCorentin Chary styleMask:NSBorderlessWindowMask 5333e230dd2SCorentin Chary backing:NSBackingStoreBuffered 5343e230dd2SCorentin Chary defer:NO]; 5355d1b2eefSProgrammingkid [fullScreenWindow setAcceptsMouseMovedEvents: YES]; 5363e230dd2SCorentin Chary [fullScreenWindow setHasShadow:NO]; 5375d1b2eefSProgrammingkid [fullScreenWindow setBackgroundColor: [NSColor blackColor]]; 5385d1b2eefSProgrammingkid [self setFrame:NSMakeRect(cx, cy, cw, ch)]; 5395d1b2eefSProgrammingkid [[fullScreenWindow contentView] addSubview: self]; 5403e230dd2SCorentin Chary [fullScreenWindow makeKeyAndOrderFront:self]; 5413e230dd2SCorentin Chary } 5423e230dd2SCorentin Chary } 5433e230dd2SCorentin Chary} 5443e230dd2SCorentin Chary 5453e230dd2SCorentin Chary- (void) handleEvent:(NSEvent *)event 5463e230dd2SCorentin Chary{ 5473e230dd2SCorentin Chary COCOA_DEBUG("QemuCocoaView: handleEvent\n"); 5483e230dd2SCorentin Chary 5493e230dd2SCorentin Chary int buttons = 0; 5503e230dd2SCorentin Chary int keycode; 55121bae11aSGerd Hoffmann bool mouse_event = false; 5523e230dd2SCorentin Chary NSPoint p = [event locationInWindow]; 5533e230dd2SCorentin Chary 5543e230dd2SCorentin Chary switch ([event type]) { 5553e230dd2SCorentin Chary case NSFlagsChanged: 5563e230dd2SCorentin Chary keycode = cocoa_keycode_to_qemu([event keyCode]); 5578895919aSPeter Maydell 55849b9bd4dSPeter Maydell if ((keycode == 219 || keycode == 220) && !isMouseGrabbed) { 5598895919aSPeter Maydell /* Don't pass command key changes to guest unless mouse is grabbed */ 5608895919aSPeter Maydell keycode = 0; 5618895919aSPeter Maydell } 5628895919aSPeter Maydell 5633e230dd2SCorentin Chary if (keycode) { 5643e230dd2SCorentin Chary if (keycode == 58 || keycode == 69) { // emulate caps lock and num lock keydown and keyup 5652e08c665SGerd Hoffmann qemu_input_event_send_key_number(dcl->con, keycode, true); 5662e08c665SGerd Hoffmann qemu_input_event_send_key_number(dcl->con, keycode, false); 56768c0aa6eSPeter Maydell } else if (qemu_console_is_graphic(NULL)) { 5683e230dd2SCorentin Chary if (modifiers_state[keycode] == 0) { // keydown 5692e08c665SGerd Hoffmann qemu_input_event_send_key_number(dcl->con, keycode, true); 5703e230dd2SCorentin Chary modifiers_state[keycode] = 1; 5713e230dd2SCorentin Chary } else { // keyup 5722e08c665SGerd Hoffmann qemu_input_event_send_key_number(dcl->con, keycode, false); 5733e230dd2SCorentin Chary modifiers_state[keycode] = 0; 5743e230dd2SCorentin Chary } 5753e230dd2SCorentin Chary } 5763e230dd2SCorentin Chary } 5773e230dd2SCorentin Chary 5783e230dd2SCorentin Chary // release Mouse grab when pressing ctrl+alt 5795d1b2eefSProgrammingkid if (([event modifierFlags] & NSControlKeyMask) && ([event modifierFlags] & NSAlternateKeyMask)) { 5803e230dd2SCorentin Chary [self ungrabMouse]; 5813e230dd2SCorentin Chary } 5823e230dd2SCorentin Chary break; 5833e230dd2SCorentin Chary case NSKeyDown: 5848895919aSPeter Maydell keycode = cocoa_keycode_to_qemu([event keyCode]); 5853e230dd2SCorentin Chary 5868895919aSPeter Maydell // forward command key combos to the host UI unless the mouse is grabbed 58749b9bd4dSPeter Maydell if (!isMouseGrabbed && ([event modifierFlags] & NSCommandKeyMask)) { 5883e230dd2SCorentin Chary [NSApp sendEvent:event]; 5893e230dd2SCorentin Chary return; 5903e230dd2SCorentin Chary } 5913e230dd2SCorentin Chary 5923e230dd2SCorentin Chary // default 5933e230dd2SCorentin Chary 5943e230dd2SCorentin Chary // handle control + alt Key Combos (ctrl+alt is reserved for QEMU) 5953e230dd2SCorentin Chary if (([event modifierFlags] & NSControlKeyMask) && ([event modifierFlags] & NSAlternateKeyMask)) { 5963e230dd2SCorentin Chary switch (keycode) { 5973e230dd2SCorentin Chary 5983e230dd2SCorentin Chary // enable graphic console 5993e230dd2SCorentin Chary case 0x02 ... 0x0a: // '1' to '9' keys 6003e230dd2SCorentin Chary console_select(keycode - 0x02); 6013e230dd2SCorentin Chary break; 6023e230dd2SCorentin Chary } 6033e230dd2SCorentin Chary 6043e230dd2SCorentin Chary // handle keys for graphic console 60568c0aa6eSPeter Maydell } else if (qemu_console_is_graphic(NULL)) { 6062e08c665SGerd Hoffmann qemu_input_event_send_key_number(dcl->con, keycode, true); 6073e230dd2SCorentin Chary 6083e230dd2SCorentin Chary // handlekeys for Monitor 6093e230dd2SCorentin Chary } else { 6103e230dd2SCorentin Chary int keysym = 0; 6113e230dd2SCorentin Chary switch([event keyCode]) { 6123e230dd2SCorentin Chary case 115: 6133e230dd2SCorentin Chary keysym = QEMU_KEY_HOME; 6143e230dd2SCorentin Chary break; 6153e230dd2SCorentin Chary case 117: 6163e230dd2SCorentin Chary keysym = QEMU_KEY_DELETE; 6173e230dd2SCorentin Chary break; 6183e230dd2SCorentin Chary case 119: 6193e230dd2SCorentin Chary keysym = QEMU_KEY_END; 6203e230dd2SCorentin Chary break; 6213e230dd2SCorentin Chary case 123: 6223e230dd2SCorentin Chary keysym = QEMU_KEY_LEFT; 6233e230dd2SCorentin Chary break; 6243e230dd2SCorentin Chary case 124: 6253e230dd2SCorentin Chary keysym = QEMU_KEY_RIGHT; 6263e230dd2SCorentin Chary break; 6273e230dd2SCorentin Chary case 125: 6283e230dd2SCorentin Chary keysym = QEMU_KEY_DOWN; 6293e230dd2SCorentin Chary break; 6303e230dd2SCorentin Chary case 126: 6313e230dd2SCorentin Chary keysym = QEMU_KEY_UP; 6323e230dd2SCorentin Chary break; 6333e230dd2SCorentin Chary default: 6343e230dd2SCorentin Chary { 6353e230dd2SCorentin Chary NSString *ks = [event characters]; 6363e230dd2SCorentin Chary if ([ks length] > 0) 6373e230dd2SCorentin Chary keysym = [ks characterAtIndex:0]; 6383e230dd2SCorentin Chary } 6393e230dd2SCorentin Chary } 6403e230dd2SCorentin Chary if (keysym) 6413e230dd2SCorentin Chary kbd_put_keysym(keysym); 6423e230dd2SCorentin Chary } 6433e230dd2SCorentin Chary break; 6443e230dd2SCorentin Chary case NSKeyUp: 6453e230dd2SCorentin Chary keycode = cocoa_keycode_to_qemu([event keyCode]); 6468895919aSPeter Maydell 6478895919aSPeter Maydell // don't pass the guest a spurious key-up if we treated this 6488895919aSPeter Maydell // command-key combo as a host UI action 64949b9bd4dSPeter Maydell if (!isMouseGrabbed && ([event modifierFlags] & NSCommandKeyMask)) { 6508895919aSPeter Maydell return; 6518895919aSPeter Maydell } 6528895919aSPeter Maydell 65368c0aa6eSPeter Maydell if (qemu_console_is_graphic(NULL)) { 6542e08c665SGerd Hoffmann qemu_input_event_send_key_number(dcl->con, keycode, false); 6553e230dd2SCorentin Chary } 6563e230dd2SCorentin Chary break; 6573e230dd2SCorentin Chary case NSMouseMoved: 6583e230dd2SCorentin Chary if (isAbsoluteEnabled) { 6595dd45beeSPeter Maydell if (![self screenContainsPoint:p] || ![[self window] isKeyWindow]) { 660f61c387eSPeter Maydell if (isMouseGrabbed) { 661f61c387eSPeter Maydell [self ungrabMouse]; 6623e230dd2SCorentin Chary } 6633e230dd2SCorentin Chary } else { 664f61c387eSPeter Maydell if (!isMouseGrabbed) { 665f61c387eSPeter Maydell [self grabMouse]; 6663e230dd2SCorentin Chary } 6673e230dd2SCorentin Chary } 6683e230dd2SCorentin Chary } 66921bae11aSGerd Hoffmann mouse_event = true; 6703e230dd2SCorentin Chary break; 6713e230dd2SCorentin Chary case NSLeftMouseDown: 6723e230dd2SCorentin Chary if ([event modifierFlags] & NSCommandKeyMask) { 6733e230dd2SCorentin Chary buttons |= MOUSE_EVENT_RBUTTON; 6743e230dd2SCorentin Chary } else { 6753e230dd2SCorentin Chary buttons |= MOUSE_EVENT_LBUTTON; 6763e230dd2SCorentin Chary } 67721bae11aSGerd Hoffmann mouse_event = true; 6783e230dd2SCorentin Chary break; 6793e230dd2SCorentin Chary case NSRightMouseDown: 6803e230dd2SCorentin Chary buttons |= MOUSE_EVENT_RBUTTON; 68121bae11aSGerd Hoffmann mouse_event = true; 6823e230dd2SCorentin Chary break; 6833e230dd2SCorentin Chary case NSOtherMouseDown: 6843e230dd2SCorentin Chary buttons |= MOUSE_EVENT_MBUTTON; 68521bae11aSGerd Hoffmann mouse_event = true; 6863e230dd2SCorentin Chary break; 6873e230dd2SCorentin Chary case NSLeftMouseDragged: 6883e230dd2SCorentin Chary if ([event modifierFlags] & NSCommandKeyMask) { 6893e230dd2SCorentin Chary buttons |= MOUSE_EVENT_RBUTTON; 6903e230dd2SCorentin Chary } else { 6913e230dd2SCorentin Chary buttons |= MOUSE_EVENT_LBUTTON; 6923e230dd2SCorentin Chary } 69321bae11aSGerd Hoffmann mouse_event = true; 6943e230dd2SCorentin Chary break; 6953e230dd2SCorentin Chary case NSRightMouseDragged: 6963e230dd2SCorentin Chary buttons |= MOUSE_EVENT_RBUTTON; 69721bae11aSGerd Hoffmann mouse_event = true; 6983e230dd2SCorentin Chary break; 6993e230dd2SCorentin Chary case NSOtherMouseDragged: 7003e230dd2SCorentin Chary buttons |= MOUSE_EVENT_MBUTTON; 70121bae11aSGerd Hoffmann mouse_event = true; 7023e230dd2SCorentin Chary break; 7033e230dd2SCorentin Chary case NSLeftMouseUp: 70421bae11aSGerd Hoffmann mouse_event = true; 705f61c387eSPeter Maydell if (!isMouseGrabbed && [self screenContainsPoint:p]) { 7063e230dd2SCorentin Chary [self grabMouse]; 7073e230dd2SCorentin Chary } 7083e230dd2SCorentin Chary break; 7093e230dd2SCorentin Chary case NSRightMouseUp: 71021bae11aSGerd Hoffmann mouse_event = true; 7113e230dd2SCorentin Chary break; 7123e230dd2SCorentin Chary case NSOtherMouseUp: 71321bae11aSGerd Hoffmann mouse_event = true; 7143e230dd2SCorentin Chary break; 7153e230dd2SCorentin Chary case NSScrollWheel: 716f61c387eSPeter Maydell if (isMouseGrabbed) { 71721bae11aSGerd Hoffmann buttons |= ([event deltaY] < 0) ? 71821bae11aSGerd Hoffmann MOUSE_EVENT_WHEELUP : MOUSE_EVENT_WHEELDN; 7193e230dd2SCorentin Chary } 720f61c387eSPeter Maydell mouse_event = true; 7213e230dd2SCorentin Chary break; 7223e230dd2SCorentin Chary default: 7233e230dd2SCorentin Chary [NSApp sendEvent:event]; 7243e230dd2SCorentin Chary } 72521bae11aSGerd Hoffmann 72621bae11aSGerd Hoffmann if (mouse_event) { 72721bae11aSGerd Hoffmann if (last_buttons != buttons) { 72821bae11aSGerd Hoffmann static uint32_t bmap[INPUT_BUTTON_MAX] = { 72921bae11aSGerd Hoffmann [INPUT_BUTTON_LEFT] = MOUSE_EVENT_LBUTTON, 73021bae11aSGerd Hoffmann [INPUT_BUTTON_MIDDLE] = MOUSE_EVENT_MBUTTON, 73121bae11aSGerd Hoffmann [INPUT_BUTTON_RIGHT] = MOUSE_EVENT_RBUTTON, 73221bae11aSGerd Hoffmann [INPUT_BUTTON_WHEEL_UP] = MOUSE_EVENT_WHEELUP, 73321bae11aSGerd Hoffmann [INPUT_BUTTON_WHEEL_DOWN] = MOUSE_EVENT_WHEELDN, 73421bae11aSGerd Hoffmann }; 73521bae11aSGerd Hoffmann qemu_input_update_buttons(dcl->con, bmap, last_buttons, buttons); 73621bae11aSGerd Hoffmann last_buttons = buttons; 73721bae11aSGerd Hoffmann } 738f61c387eSPeter Maydell if (isMouseGrabbed) { 739f61c387eSPeter Maydell if (isAbsoluteEnabled) { 740f61c387eSPeter Maydell /* Note that the origin for Cocoa mouse coords is bottom left, not top left. 741f61c387eSPeter Maydell * The check on screenContainsPoint is to avoid sending out of range values for 742f61c387eSPeter Maydell * clicks in the titlebar. 743f61c387eSPeter Maydell */ 744f61c387eSPeter Maydell if ([self screenContainsPoint:p]) { 74521bae11aSGerd Hoffmann qemu_input_queue_abs(dcl->con, INPUT_AXIS_X, p.x, screen.width); 746f61c387eSPeter Maydell qemu_input_queue_abs(dcl->con, INPUT_AXIS_Y, screen.height - p.y, screen.height); 747f61c387eSPeter Maydell } 748f61c387eSPeter Maydell } else { 74921bae11aSGerd Hoffmann qemu_input_queue_rel(dcl->con, INPUT_AXIS_X, (int)[event deltaX]); 75021bae11aSGerd Hoffmann qemu_input_queue_rel(dcl->con, INPUT_AXIS_Y, (int)[event deltaY]); 751f61c387eSPeter Maydell } 75221bae11aSGerd Hoffmann } else { 75321bae11aSGerd Hoffmann [NSApp sendEvent:event]; 75421bae11aSGerd Hoffmann } 75521bae11aSGerd Hoffmann qemu_input_event_sync(); 75621bae11aSGerd Hoffmann } 7573e230dd2SCorentin Chary} 7583e230dd2SCorentin Chary 7593e230dd2SCorentin Chary- (void) grabMouse 7603e230dd2SCorentin Chary{ 7613e230dd2SCorentin Chary COCOA_DEBUG("QemuCocoaView: grabMouse\n"); 7623e230dd2SCorentin Chary 7633e230dd2SCorentin Chary if (!isFullscreen) { 7643e230dd2SCorentin Chary if (qemu_name) 7653e230dd2SCorentin Chary [normalWindow setTitle:[NSString stringWithFormat:@"QEMU %s - (Press ctrl + alt to release Mouse)", qemu_name]]; 7663e230dd2SCorentin Chary else 7673e230dd2SCorentin Chary [normalWindow setTitle:@"QEMU - (Press ctrl + alt to release Mouse)"]; 7683e230dd2SCorentin Chary } 76913aefd30SPeter Maydell [self hideCursor]; 770f61c387eSPeter Maydell if (!isAbsoluteEnabled) { 771f61c387eSPeter Maydell isMouseDeassociated = TRUE; 7723e230dd2SCorentin Chary CGAssociateMouseAndMouseCursorPosition(FALSE); 773f61c387eSPeter Maydell } 77449b9bd4dSPeter Maydell isMouseGrabbed = TRUE; // while isMouseGrabbed = TRUE, QemuCocoaApp sends all events to [cocoaView handleEvent:] 7753e230dd2SCorentin Chary} 7763e230dd2SCorentin Chary 7773e230dd2SCorentin Chary- (void) ungrabMouse 7783e230dd2SCorentin Chary{ 7793e230dd2SCorentin Chary COCOA_DEBUG("QemuCocoaView: ungrabMouse\n"); 7803e230dd2SCorentin Chary 7813e230dd2SCorentin Chary if (!isFullscreen) { 7823e230dd2SCorentin Chary if (qemu_name) 7833e230dd2SCorentin Chary [normalWindow setTitle:[NSString stringWithFormat:@"QEMU %s", qemu_name]]; 7843e230dd2SCorentin Chary else 7853e230dd2SCorentin Chary [normalWindow setTitle:@"QEMU"]; 7863e230dd2SCorentin Chary } 78713aefd30SPeter Maydell [self unhideCursor]; 788f61c387eSPeter Maydell if (isMouseDeassociated) { 7893e230dd2SCorentin Chary CGAssociateMouseAndMouseCursorPosition(TRUE); 790f61c387eSPeter Maydell isMouseDeassociated = FALSE; 791f61c387eSPeter Maydell } 79249b9bd4dSPeter Maydell isMouseGrabbed = FALSE; 7933e230dd2SCorentin Chary} 7943e230dd2SCorentin Chary 7953e230dd2SCorentin Chary- (void) setAbsoluteEnabled:(BOOL)tIsAbsoluteEnabled {isAbsoluteEnabled = tIsAbsoluteEnabled;} 79649b9bd4dSPeter Maydell- (BOOL) isMouseGrabbed {return isMouseGrabbed;} 7973e230dd2SCorentin Chary- (BOOL) isAbsoluteEnabled {return isAbsoluteEnabled;} 798f61c387eSPeter Maydell- (BOOL) isMouseDeassociated {return isMouseDeassociated;} 7993e230dd2SCorentin Chary- (float) cdx {return cdx;} 8003e230dd2SCorentin Chary- (float) cdy {return cdy;} 8013e230dd2SCorentin Chary- (QEMUScreen) gscreen {return screen;} 802*3b178b71SJohn Arbuckle 803*3b178b71SJohn Arbuckle/* 804*3b178b71SJohn Arbuckle * Makes the target think all down keys are being released. 805*3b178b71SJohn Arbuckle * This prevents a stuck key problem, since we will not see 806*3b178b71SJohn Arbuckle * key up events for those keys after we have lost focus. 807*3b178b71SJohn Arbuckle */ 808*3b178b71SJohn Arbuckle- (void) raiseAllKeys 809*3b178b71SJohn Arbuckle{ 810*3b178b71SJohn Arbuckle int index; 811*3b178b71SJohn Arbuckle const int max_index = ARRAY_SIZE(modifiers_state); 812*3b178b71SJohn Arbuckle 813*3b178b71SJohn Arbuckle for (index = 0; index < max_index; index++) { 814*3b178b71SJohn Arbuckle if (modifiers_state[index]) { 815*3b178b71SJohn Arbuckle modifiers_state[index] = 0; 816*3b178b71SJohn Arbuckle qemu_input_event_send_key_number(dcl->con, index, false); 817*3b178b71SJohn Arbuckle } 818*3b178b71SJohn Arbuckle } 819*3b178b71SJohn Arbuckle} 8203e230dd2SCorentin Chary@end 8213e230dd2SCorentin Chary 8223e230dd2SCorentin Chary 8233e230dd2SCorentin Chary 8243e230dd2SCorentin Chary/* 8253e230dd2SCorentin Chary ------------------------------------------------------ 8263e230dd2SCorentin Chary QemuCocoaAppController 8273e230dd2SCorentin Chary ------------------------------------------------------ 8283e230dd2SCorentin Chary*/ 8293e230dd2SCorentin Chary@interface QemuCocoaAppController : NSObject 8302a4c8c53SPeter Maydell#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6) 831d9bc14f6SJohn Arbuckle <NSWindowDelegate, NSApplicationDelegate> 8322a4c8c53SPeter Maydell#endif 8333e230dd2SCorentin Chary{ 8343e230dd2SCorentin Chary} 8353e230dd2SCorentin Chary- (void)startEmulationWithArgc:(int)argc argv:(char**)argv; 836de1aadeeSPeter Maydell- (void)openPanelDidEnd:(NSOpenPanel *)sheet returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo; 8375d1b2eefSProgrammingkid- (void)doToggleFullScreen:(id)sender; 8383e230dd2SCorentin Chary- (void)toggleFullScreen:(id)sender; 8393e230dd2SCorentin Chary- (void)showQEMUDoc:(id)sender; 8403e230dd2SCorentin Chary- (void)showQEMUTec:(id)sender; 8415d1b2eefSProgrammingkid- (void)zoomToFit:(id) sender; 842b4c6a112SProgrammingkid- (void)displayConsole:(id)sender; 8438524f1c7SJohn Arbuckle- (void)pauseQEMU:(id)sender; 8448524f1c7SJohn Arbuckle- (void)resumeQEMU:(id)sender; 8458524f1c7SJohn Arbuckle- (void)displayPause; 8468524f1c7SJohn Arbuckle- (void)removePause; 84727074614SJohn Arbuckle- (void)restartQEMU:(id)sender; 84827074614SJohn Arbuckle- (void)powerDownQEMU:(id)sender; 849693a3e01SJohn Arbuckle- (void)ejectDeviceMedia:(id)sender; 850693a3e01SJohn Arbuckle- (void)changeDeviceMedia:(id)sender; 851d9bc14f6SJohn Arbuckle- (BOOL)verifyQuit; 8523e230dd2SCorentin Chary@end 8533e230dd2SCorentin Chary 8543e230dd2SCorentin Chary@implementation QemuCocoaAppController 8553e230dd2SCorentin Chary- (id) init 8563e230dd2SCorentin Chary{ 8573e230dd2SCorentin Chary COCOA_DEBUG("QemuCocoaAppController: init\n"); 8583e230dd2SCorentin Chary 8593e230dd2SCorentin Chary self = [super init]; 8603e230dd2SCorentin Chary if (self) { 8613e230dd2SCorentin Chary 8623e230dd2SCorentin Chary // create a view and add it to the window 8633e230dd2SCorentin Chary cocoaView = [[QemuCocoaView alloc] initWithFrame:NSMakeRect(0.0, 0.0, 640.0, 480.0)]; 8643e230dd2SCorentin Chary if(!cocoaView) { 8653e230dd2SCorentin Chary fprintf(stderr, "(cocoa) can't create a view\n"); 8663e230dd2SCorentin Chary exit(1); 8673e230dd2SCorentin Chary } 8683e230dd2SCorentin Chary 8693e230dd2SCorentin Chary // create a window 8703e230dd2SCorentin Chary normalWindow = [[NSWindow alloc] initWithContentRect:[cocoaView frame] 8713e230dd2SCorentin Chary styleMask:NSTitledWindowMask|NSMiniaturizableWindowMask|NSClosableWindowMask 8723e230dd2SCorentin Chary backing:NSBackingStoreBuffered defer:NO]; 8733e230dd2SCorentin Chary if(!normalWindow) { 8743e230dd2SCorentin Chary fprintf(stderr, "(cocoa) can't create window\n"); 8753e230dd2SCorentin Chary exit(1); 8763e230dd2SCorentin Chary } 8773e230dd2SCorentin Chary [normalWindow setAcceptsMouseMovedEvents:YES]; 8783e230dd2SCorentin Chary [normalWindow setTitle:[NSString stringWithFormat:@"QEMU"]]; 8793e230dd2SCorentin Chary [normalWindow setContentView:cocoaView]; 88081801ae2SPeter Maydell#if (MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_10) 8813e230dd2SCorentin Chary [normalWindow useOptimizedDrawing:YES]; 88281801ae2SPeter Maydell#endif 8833e230dd2SCorentin Chary [normalWindow makeKeyAndOrderFront:self]; 8843e230dd2SCorentin Chary [normalWindow center]; 885d9bc14f6SJohn Arbuckle [normalWindow setDelegate: self]; 8865d1b2eefSProgrammingkid stretch_video = false; 8878524f1c7SJohn Arbuckle 8888524f1c7SJohn Arbuckle /* Used for displaying pause on the screen */ 8898524f1c7SJohn Arbuckle pauseLabel = [NSTextField new]; 8908524f1c7SJohn Arbuckle [pauseLabel setBezeled:YES]; 8918524f1c7SJohn Arbuckle [pauseLabel setDrawsBackground:YES]; 8928524f1c7SJohn Arbuckle [pauseLabel setBackgroundColor: [NSColor whiteColor]]; 8938524f1c7SJohn Arbuckle [pauseLabel setEditable:NO]; 8948524f1c7SJohn Arbuckle [pauseLabel setSelectable:NO]; 8958524f1c7SJohn Arbuckle [pauseLabel setStringValue: @"Paused"]; 8968524f1c7SJohn Arbuckle [pauseLabel setFont: [NSFont fontWithName: @"Helvetica" size: 90]]; 8978524f1c7SJohn Arbuckle [pauseLabel setTextColor: [NSColor blackColor]]; 8988524f1c7SJohn Arbuckle [pauseLabel sizeToFit]; 899693a3e01SJohn Arbuckle 900693a3e01SJohn Arbuckle // set the supported image file types that can be opened 901693a3e01SJohn Arbuckle supportedImageFileTypes = [NSArray arrayWithObjects: @"img", @"iso", @"dmg", 902693a3e01SJohn Arbuckle @"qcow", @"qcow2", @"cloop", @"vmdk", nil]; 9033e230dd2SCorentin Chary } 9043e230dd2SCorentin Chary return self; 9053e230dd2SCorentin Chary} 9063e230dd2SCorentin Chary 9073e230dd2SCorentin Chary- (void) dealloc 9083e230dd2SCorentin Chary{ 9093e230dd2SCorentin Chary COCOA_DEBUG("QemuCocoaAppController: dealloc\n"); 9103e230dd2SCorentin Chary 9113e230dd2SCorentin Chary if (cocoaView) 9123e230dd2SCorentin Chary [cocoaView release]; 9133e230dd2SCorentin Chary [super dealloc]; 9143e230dd2SCorentin Chary} 9153e230dd2SCorentin Chary 9163e230dd2SCorentin Chary- (void)applicationDidFinishLaunching: (NSNotification *) note 9173e230dd2SCorentin Chary{ 9183e230dd2SCorentin Chary COCOA_DEBUG("QemuCocoaAppController: applicationDidFinishLaunching\n"); 9193e230dd2SCorentin Chary 92049b9bd4dSPeter Maydell // Display an open dialog box if no arguments were passed or 9213e230dd2SCorentin Chary // if qemu was launched from the finder ( the Finder passes "-psn" ) 9223e230dd2SCorentin Chary if( gArgc <= 1 || strncmp ((char *)gArgv[1], "-psn", 4) == 0) { 9233e230dd2SCorentin Chary NSOpenPanel *op = [[NSOpenPanel alloc] init]; 9243e230dd2SCorentin Chary [op setPrompt:@"Boot image"]; 9253e230dd2SCorentin Chary [op setMessage:@"Select the disk image you want to boot.\n\nHit the \"Cancel\" button to quit"]; 9262ba9de6eSPeter Maydell#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6) 927693a3e01SJohn Arbuckle [op setAllowedFileTypes:supportedImageFileTypes]; 9282ba9de6eSPeter Maydell [op beginSheetModalForWindow:normalWindow 9292ba9de6eSPeter Maydell completionHandler:^(NSInteger returnCode) 9302ba9de6eSPeter Maydell { [self openPanelDidEnd:op 9312ba9de6eSPeter Maydell returnCode:returnCode contextInfo:NULL ]; } ]; 9322ba9de6eSPeter Maydell#else 9332ba9de6eSPeter Maydell // Compatibility code for pre-10.6, using deprecated method 9342ba9de6eSPeter Maydell [op beginSheetForDirectory:nil file:nil types:filetypes 9353e230dd2SCorentin Chary modalForWindow:normalWindow modalDelegate:self 9363e230dd2SCorentin Chary didEndSelector:@selector(openPanelDidEnd:returnCode:contextInfo:) contextInfo:NULL]; 9372ba9de6eSPeter Maydell#endif 9383e230dd2SCorentin Chary } else { 9395cbdb3a3SStefan Weil // or launch QEMU, with the global args 9403e230dd2SCorentin Chary [self startEmulationWithArgc:gArgc argv:(char **)gArgv]; 9413e230dd2SCorentin Chary } 9423e230dd2SCorentin Chary} 9433e230dd2SCorentin Chary 9443e230dd2SCorentin Chary- (void)applicationWillTerminate:(NSNotification *)aNotification 9453e230dd2SCorentin Chary{ 9463e230dd2SCorentin Chary COCOA_DEBUG("QemuCocoaAppController: applicationWillTerminate\n"); 9473e230dd2SCorentin Chary 9483e230dd2SCorentin Chary qemu_system_shutdown_request(); 9493e230dd2SCorentin Chary exit(0); 9503e230dd2SCorentin Chary} 9513e230dd2SCorentin Chary 9523e230dd2SCorentin Chary- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)theApplication 9533e230dd2SCorentin Chary{ 9543e230dd2SCorentin Chary return YES; 9553e230dd2SCorentin Chary} 9563e230dd2SCorentin Chary 957d9bc14f6SJohn Arbuckle- (NSApplicationTerminateReply)applicationShouldTerminate: 958d9bc14f6SJohn Arbuckle (NSApplication *)sender 959d9bc14f6SJohn Arbuckle{ 960d9bc14f6SJohn Arbuckle COCOA_DEBUG("QemuCocoaAppController: applicationShouldTerminate\n"); 961d9bc14f6SJohn Arbuckle return [self verifyQuit]; 962d9bc14f6SJohn Arbuckle} 963d9bc14f6SJohn Arbuckle 964d9bc14f6SJohn Arbuckle/* Called when the user clicks on a window's close button */ 965d9bc14f6SJohn Arbuckle- (BOOL)windowShouldClose:(id)sender 966d9bc14f6SJohn Arbuckle{ 967d9bc14f6SJohn Arbuckle COCOA_DEBUG("QemuCocoaAppController: windowShouldClose\n"); 968d9bc14f6SJohn Arbuckle [NSApp terminate: sender]; 969d9bc14f6SJohn Arbuckle /* If the user allows the application to quit then the call to 970d9bc14f6SJohn Arbuckle * NSApp terminate will never return. If we get here then the user 971d9bc14f6SJohn Arbuckle * cancelled the quit, so we should return NO to not permit the 972d9bc14f6SJohn Arbuckle * closing of this window. 973d9bc14f6SJohn Arbuckle */ 974d9bc14f6SJohn Arbuckle return NO; 975d9bc14f6SJohn Arbuckle} 976d9bc14f6SJohn Arbuckle 977*3b178b71SJohn Arbuckle/* Called when QEMU goes into the background */ 978*3b178b71SJohn Arbuckle- (void) applicationWillResignActive: (NSNotification *)aNotification 979*3b178b71SJohn Arbuckle{ 980*3b178b71SJohn Arbuckle COCOA_DEBUG("QemuCocoaAppController: applicationWillResignActive\n"); 981*3b178b71SJohn Arbuckle [cocoaView raiseAllKeys]; 982*3b178b71SJohn Arbuckle} 983*3b178b71SJohn Arbuckle 9843e230dd2SCorentin Chary- (void)startEmulationWithArgc:(int)argc argv:(char**)argv 9853e230dd2SCorentin Chary{ 9863e230dd2SCorentin Chary COCOA_DEBUG("QemuCocoaAppController: startEmulationWithArgc\n"); 9873e230dd2SCorentin Chary 9883e230dd2SCorentin Chary int status; 9893bbbee18SAndreas Färber status = qemu_main(argc, argv, *_NSGetEnviron()); 9903e230dd2SCorentin Chary exit(status); 9913e230dd2SCorentin Chary} 9923e230dd2SCorentin Chary 993de1aadeeSPeter Maydell- (void)openPanelDidEnd:(NSOpenPanel *)sheet returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo 9943e230dd2SCorentin Chary{ 9953e230dd2SCorentin Chary COCOA_DEBUG("QemuCocoaAppController: openPanelDidEnd\n"); 9963e230dd2SCorentin Chary 9978617989eSPeter Maydell /* The NSFileHandlingPanelOKButton/NSFileHandlingPanelCancelButton values for 9988617989eSPeter Maydell * returnCode strictly only apply for the 10.6-and-up beginSheetModalForWindow 9998617989eSPeter Maydell * API. For the legacy pre-10.6 beginSheetForDirectory API they are NSOKButton 10008617989eSPeter Maydell * and NSCancelButton. However conveniently the values are the same. 10018617989eSPeter Maydell * We use the non-legacy names because the others are deprecated in OSX 10.10. 10028617989eSPeter Maydell */ 10038617989eSPeter Maydell if (returnCode == NSFileHandlingPanelCancelButton) { 10043e230dd2SCorentin Chary exit(0); 10058617989eSPeter Maydell } else if (returnCode == NSFileHandlingPanelOKButton) { 10068bb3f1e3SPeter Maydell char *img = (char*)[ [ [ sheet URL ] path ] cStringUsingEncoding:NSASCIIStringEncoding]; 10073e230dd2SCorentin Chary 100898db429dSPeter Maydell char **argv = g_new(char *, 4); 10093e230dd2SCorentin Chary 101013766eb1SAndreas Färber [sheet close]; 101113766eb1SAndreas Färber 101298db429dSPeter Maydell argv[0] = g_strdup(gArgv[0]); 101398db429dSPeter Maydell argv[1] = g_strdup("-hda"); 101498db429dSPeter Maydell argv[2] = g_strdup(img); 101598db429dSPeter Maydell argv[3] = NULL; 10163e230dd2SCorentin Chary 101798db429dSPeter Maydell // printf("Using argc %d argv %s -hda %s\n", 3, gArgv[0], img); 10183e230dd2SCorentin Chary 10193e230dd2SCorentin Chary [self startEmulationWithArgc:3 argv:(char**)argv]; 10203e230dd2SCorentin Chary } 10213e230dd2SCorentin Chary} 10225d1b2eefSProgrammingkid 10235d1b2eefSProgrammingkid/* We abstract the method called by the Enter Fullscreen menu item 10245d1b2eefSProgrammingkid * because Mac OS 10.7 and higher disables it. This is because of the 10255d1b2eefSProgrammingkid * menu item's old selector's name toggleFullScreen: 10265d1b2eefSProgrammingkid */ 10275d1b2eefSProgrammingkid- (void) doToggleFullScreen:(id)sender 10285d1b2eefSProgrammingkid{ 10295d1b2eefSProgrammingkid [self toggleFullScreen:(id)sender]; 10305d1b2eefSProgrammingkid} 10315d1b2eefSProgrammingkid 10323e230dd2SCorentin Chary- (void)toggleFullScreen:(id)sender 10333e230dd2SCorentin Chary{ 10343e230dd2SCorentin Chary COCOA_DEBUG("QemuCocoaAppController: toggleFullScreen\n"); 10353e230dd2SCorentin Chary 10363e230dd2SCorentin Chary [cocoaView toggleFullScreen:sender]; 10373e230dd2SCorentin Chary} 10383e230dd2SCorentin Chary 10393e230dd2SCorentin Chary- (void)showQEMUDoc:(id)sender 10403e230dd2SCorentin Chary{ 10413e230dd2SCorentin Chary COCOA_DEBUG("QemuCocoaAppController: showQEMUDoc\n"); 10423e230dd2SCorentin Chary 10433e230dd2SCorentin Chary [[NSWorkspace sharedWorkspace] openFile:[NSString stringWithFormat:@"%@/../doc/qemu/qemu-doc.html", 10443e230dd2SCorentin Chary [[NSBundle mainBundle] resourcePath]] withApplication:@"Help Viewer"]; 10453e230dd2SCorentin Chary} 10463e230dd2SCorentin Chary 10473e230dd2SCorentin Chary- (void)showQEMUTec:(id)sender 10483e230dd2SCorentin Chary{ 10493e230dd2SCorentin Chary COCOA_DEBUG("QemuCocoaAppController: showQEMUTec\n"); 10503e230dd2SCorentin Chary 10513e230dd2SCorentin Chary [[NSWorkspace sharedWorkspace] openFile:[NSString stringWithFormat:@"%@/../doc/qemu/qemu-tech.html", 10523e230dd2SCorentin Chary [[NSBundle mainBundle] resourcePath]] withApplication:@"Help Viewer"]; 10533e230dd2SCorentin Chary} 10545d1b2eefSProgrammingkid 10555d1b2eefSProgrammingkid/* Stretches video to fit host monitor size */ 10565d1b2eefSProgrammingkid- (void)zoomToFit:(id) sender 10575d1b2eefSProgrammingkid{ 10585d1b2eefSProgrammingkid stretch_video = !stretch_video; 10595d1b2eefSProgrammingkid if (stretch_video == true) { 10605d1b2eefSProgrammingkid [sender setState: NSOnState]; 10615d1b2eefSProgrammingkid } else { 10625d1b2eefSProgrammingkid [sender setState: NSOffState]; 10635d1b2eefSProgrammingkid } 10645d1b2eefSProgrammingkid} 10653e230dd2SCorentin Chary 1066b4c6a112SProgrammingkid/* Displays the console on the screen */ 1067b4c6a112SProgrammingkid- (void)displayConsole:(id)sender 1068b4c6a112SProgrammingkid{ 1069b4c6a112SProgrammingkid console_select([sender tag]); 1070b4c6a112SProgrammingkid} 10718524f1c7SJohn Arbuckle 10728524f1c7SJohn Arbuckle/* Pause the guest */ 10738524f1c7SJohn Arbuckle- (void)pauseQEMU:(id)sender 10748524f1c7SJohn Arbuckle{ 10758524f1c7SJohn Arbuckle qmp_stop(NULL); 10768524f1c7SJohn Arbuckle [sender setEnabled: NO]; 10778524f1c7SJohn Arbuckle [[[sender menu] itemWithTitle: @"Resume"] setEnabled: YES]; 10788524f1c7SJohn Arbuckle [self displayPause]; 10798524f1c7SJohn Arbuckle} 10808524f1c7SJohn Arbuckle 10818524f1c7SJohn Arbuckle/* Resume running the guest operating system */ 10828524f1c7SJohn Arbuckle- (void)resumeQEMU:(id) sender 10838524f1c7SJohn Arbuckle{ 10848524f1c7SJohn Arbuckle qmp_cont(NULL); 10858524f1c7SJohn Arbuckle [sender setEnabled: NO]; 10868524f1c7SJohn Arbuckle [[[sender menu] itemWithTitle: @"Pause"] setEnabled: YES]; 10878524f1c7SJohn Arbuckle [self removePause]; 10888524f1c7SJohn Arbuckle} 10898524f1c7SJohn Arbuckle 10908524f1c7SJohn Arbuckle/* Displays the word pause on the screen */ 10918524f1c7SJohn Arbuckle- (void)displayPause 10928524f1c7SJohn Arbuckle{ 10938524f1c7SJohn Arbuckle /* Coordinates have to be calculated each time because the window can change its size */ 10948524f1c7SJohn Arbuckle int xCoord, yCoord, width, height; 10958524f1c7SJohn Arbuckle xCoord = ([normalWindow frame].size.width - [pauseLabel frame].size.width)/2; 10968524f1c7SJohn Arbuckle yCoord = [normalWindow frame].size.height - [pauseLabel frame].size.height - ([pauseLabel frame].size.height * .5); 10978524f1c7SJohn Arbuckle width = [pauseLabel frame].size.width; 10988524f1c7SJohn Arbuckle height = [pauseLabel frame].size.height; 10998524f1c7SJohn Arbuckle [pauseLabel setFrame: NSMakeRect(xCoord, yCoord, width, height)]; 11008524f1c7SJohn Arbuckle [cocoaView addSubview: pauseLabel]; 11018524f1c7SJohn Arbuckle} 11028524f1c7SJohn Arbuckle 11038524f1c7SJohn Arbuckle/* Removes the word pause from the screen */ 11048524f1c7SJohn Arbuckle- (void)removePause 11058524f1c7SJohn Arbuckle{ 11068524f1c7SJohn Arbuckle [pauseLabel removeFromSuperview]; 11078524f1c7SJohn Arbuckle} 11088524f1c7SJohn Arbuckle 110927074614SJohn Arbuckle/* Restarts QEMU */ 111027074614SJohn Arbuckle- (void)restartQEMU:(id)sender 111127074614SJohn Arbuckle{ 111227074614SJohn Arbuckle qmp_system_reset(NULL); 111327074614SJohn Arbuckle} 111427074614SJohn Arbuckle 111527074614SJohn Arbuckle/* Powers down QEMU */ 111627074614SJohn Arbuckle- (void)powerDownQEMU:(id)sender 111727074614SJohn Arbuckle{ 111827074614SJohn Arbuckle qmp_system_powerdown(NULL); 111927074614SJohn Arbuckle} 112027074614SJohn Arbuckle 1121693a3e01SJohn Arbuckle/* Ejects the media. 1122693a3e01SJohn Arbuckle * Uses sender's tag to figure out the device to eject. 1123693a3e01SJohn Arbuckle */ 1124693a3e01SJohn Arbuckle- (void)ejectDeviceMedia:(id)sender 1125693a3e01SJohn Arbuckle{ 1126693a3e01SJohn Arbuckle NSString * drive; 1127693a3e01SJohn Arbuckle drive = [sender representedObject]; 1128693a3e01SJohn Arbuckle if(drive == nil) { 1129693a3e01SJohn Arbuckle NSBeep(); 1130693a3e01SJohn Arbuckle QEMU_Alert(@"Failed to find drive to eject!"); 1131693a3e01SJohn Arbuckle return; 1132693a3e01SJohn Arbuckle } 1133693a3e01SJohn Arbuckle 1134693a3e01SJohn Arbuckle Error *err = NULL; 1135693a3e01SJohn Arbuckle qmp_eject([drive cStringUsingEncoding: NSASCIIStringEncoding], false, false, &err); 1136693a3e01SJohn Arbuckle handleAnyDeviceErrors(err); 1137693a3e01SJohn Arbuckle} 1138693a3e01SJohn Arbuckle 1139693a3e01SJohn Arbuckle/* Displays a dialog box asking the user to select an image file to load. 1140693a3e01SJohn Arbuckle * Uses sender's represented object value to figure out which drive to use. 1141693a3e01SJohn Arbuckle */ 1142693a3e01SJohn Arbuckle- (void)changeDeviceMedia:(id)sender 1143693a3e01SJohn Arbuckle{ 1144693a3e01SJohn Arbuckle /* Find the drive name */ 1145693a3e01SJohn Arbuckle NSString * drive; 1146693a3e01SJohn Arbuckle drive = [sender representedObject]; 1147693a3e01SJohn Arbuckle if(drive == nil) { 1148693a3e01SJohn Arbuckle NSBeep(); 1149693a3e01SJohn Arbuckle QEMU_Alert(@"Could not find drive!"); 1150693a3e01SJohn Arbuckle return; 1151693a3e01SJohn Arbuckle } 1152693a3e01SJohn Arbuckle 1153693a3e01SJohn Arbuckle /* Display the file open dialog */ 1154693a3e01SJohn Arbuckle NSOpenPanel * openPanel; 1155693a3e01SJohn Arbuckle openPanel = [NSOpenPanel openPanel]; 1156693a3e01SJohn Arbuckle [openPanel setCanChooseFiles: YES]; 1157693a3e01SJohn Arbuckle [openPanel setAllowsMultipleSelection: NO]; 1158693a3e01SJohn Arbuckle [openPanel setAllowedFileTypes: supportedImageFileTypes]; 1159693a3e01SJohn Arbuckle if([openPanel runModal] == NSFileHandlingPanelOKButton) { 1160693a3e01SJohn Arbuckle NSString * file = [[[openPanel URLs] objectAtIndex: 0] path]; 1161693a3e01SJohn Arbuckle if(file == nil) { 1162693a3e01SJohn Arbuckle NSBeep(); 1163693a3e01SJohn Arbuckle QEMU_Alert(@"Failed to convert URL to file path!"); 1164693a3e01SJohn Arbuckle return; 1165693a3e01SJohn Arbuckle } 1166693a3e01SJohn Arbuckle 1167693a3e01SJohn Arbuckle Error *err = NULL; 1168693a3e01SJohn Arbuckle qmp_change_blockdev([drive cStringUsingEncoding: NSASCIIStringEncoding], 1169693a3e01SJohn Arbuckle [file cStringUsingEncoding: NSASCIIStringEncoding], 1170693a3e01SJohn Arbuckle "raw", 1171693a3e01SJohn Arbuckle &err); 1172693a3e01SJohn Arbuckle handleAnyDeviceErrors(err); 1173693a3e01SJohn Arbuckle } 1174693a3e01SJohn Arbuckle} 1175693a3e01SJohn Arbuckle 1176d9bc14f6SJohn Arbuckle/* Verifies if the user really wants to quit */ 1177d9bc14f6SJohn Arbuckle- (BOOL)verifyQuit 1178d9bc14f6SJohn Arbuckle{ 1179d9bc14f6SJohn Arbuckle NSAlert *alert = [NSAlert new]; 1180d9bc14f6SJohn Arbuckle [alert autorelease]; 1181d9bc14f6SJohn Arbuckle [alert setMessageText: @"Are you sure you want to quit QEMU?"]; 1182d9bc14f6SJohn Arbuckle [alert addButtonWithTitle: @"Cancel"]; 1183d9bc14f6SJohn Arbuckle [alert addButtonWithTitle: @"Quit"]; 1184d9bc14f6SJohn Arbuckle if([alert runModal] == NSAlertSecondButtonReturn) { 1185d9bc14f6SJohn Arbuckle return YES; 1186d9bc14f6SJohn Arbuckle } else { 1187d9bc14f6SJohn Arbuckle return NO; 1188d9bc14f6SJohn Arbuckle } 1189d9bc14f6SJohn Arbuckle} 1190d9bc14f6SJohn Arbuckle 1191b4c6a112SProgrammingkid@end 11923e230dd2SCorentin Chary 11933e230dd2SCorentin Chary 11943e230dd2SCorentin Charyint main (int argc, const char * argv[]) { 11953e230dd2SCorentin Chary 11963e230dd2SCorentin Chary gArgc = argc; 11973e230dd2SCorentin Chary gArgv = (char **)argv; 11983e230dd2SCorentin Chary int i; 11993e230dd2SCorentin Chary 12003e230dd2SCorentin Chary /* In case we don't need to display a window, let's not do that */ 12013e230dd2SCorentin Chary for (i = 1; i < argc; i++) { 1202e4ebcc1aSTristan Gingold const char *opt = argv[i]; 1203e4ebcc1aSTristan Gingold 1204e4ebcc1aSTristan Gingold if (opt[0] == '-') { 1205e4ebcc1aSTristan Gingold /* Treat --foo the same as -foo. */ 1206e4ebcc1aSTristan Gingold if (opt[1] == '-') { 1207e4ebcc1aSTristan Gingold opt++; 1208e4ebcc1aSTristan Gingold } 12099851484fSAlexandre Raymond if (!strcmp(opt, "-h") || !strcmp(opt, "-help") || 12109851484fSAlexandre Raymond !strcmp(opt, "-vnc") || 1211e4ebcc1aSTristan Gingold !strcmp(opt, "-nographic") || 1212e4ebcc1aSTristan Gingold !strcmp(opt, "-version") || 121360b46aa2SAndreas Färber !strcmp(opt, "-curses") || 1214b12a84ceSRainer Müller !strcmp(opt, "-display") || 121560b46aa2SAndreas Färber !strcmp(opt, "-qtest")) { 12163bbbee18SAndreas Färber return qemu_main(gArgc, gArgv, *_NSGetEnviron()); 12173e230dd2SCorentin Chary } 12183e230dd2SCorentin Chary } 1219e4ebcc1aSTristan Gingold } 12203e230dd2SCorentin Chary 12213e230dd2SCorentin Chary NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; 12223e230dd2SCorentin Chary 122342a5dfe7SPeter Maydell // Pull this console process up to being a fully-fledged graphical 122442a5dfe7SPeter Maydell // app with a menubar and Dock icon 122542a5dfe7SPeter Maydell ProcessSerialNumber psn = { 0, kCurrentProcess }; 122642a5dfe7SPeter Maydell TransformProcessType(&psn, kProcessTransformToForegroundApplication); 122742a5dfe7SPeter Maydell 12283e230dd2SCorentin Chary [NSApplication sharedApplication]; 12293e230dd2SCorentin Chary 12303e230dd2SCorentin Chary // Add menus 12313e230dd2SCorentin Chary NSMenu *menu; 12323e230dd2SCorentin Chary NSMenuItem *menuItem; 12333e230dd2SCorentin Chary 12343e230dd2SCorentin Chary [NSApp setMainMenu:[[NSMenu alloc] init]]; 12353e230dd2SCorentin Chary 12363e230dd2SCorentin Chary // Application menu 12373e230dd2SCorentin Chary menu = [[NSMenu alloc] initWithTitle:@""]; 12383e230dd2SCorentin Chary [menu addItemWithTitle:@"About QEMU" action:@selector(orderFrontStandardAboutPanel:) keyEquivalent:@""]; // About QEMU 12393e230dd2SCorentin Chary [menu addItem:[NSMenuItem separatorItem]]; //Separator 12403e230dd2SCorentin Chary [menu addItemWithTitle:@"Hide QEMU" action:@selector(hide:) keyEquivalent:@"h"]; //Hide QEMU 12413e230dd2SCorentin Chary menuItem = (NSMenuItem *)[menu addItemWithTitle:@"Hide Others" action:@selector(hideOtherApplications:) keyEquivalent:@"h"]; // Hide Others 12423e230dd2SCorentin Chary [menuItem setKeyEquivalentModifierMask:(NSAlternateKeyMask|NSCommandKeyMask)]; 12433e230dd2SCorentin Chary [menu addItemWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""]; // Show All 12443e230dd2SCorentin Chary [menu addItem:[NSMenuItem separatorItem]]; //Separator 12453e230dd2SCorentin Chary [menu addItemWithTitle:@"Quit QEMU" action:@selector(terminate:) keyEquivalent:@"q"]; 12463e230dd2SCorentin Chary menuItem = [[NSMenuItem alloc] initWithTitle:@"Apple" action:nil keyEquivalent:@""]; 12473e230dd2SCorentin Chary [menuItem setSubmenu:menu]; 12483e230dd2SCorentin Chary [[NSApp mainMenu] addItem:menuItem]; 12493e230dd2SCorentin Chary [NSApp performSelector:@selector(setAppleMenu:) withObject:menu]; // Workaround (this method is private since 10.4+) 12503e230dd2SCorentin Chary 12518524f1c7SJohn Arbuckle // Machine menu 12528524f1c7SJohn Arbuckle menu = [[NSMenu alloc] initWithTitle: @"Machine"]; 12538524f1c7SJohn Arbuckle [menu setAutoenablesItems: NO]; 12548524f1c7SJohn Arbuckle [menu addItem: [[[NSMenuItem alloc] initWithTitle: @"Pause" action: @selector(pauseQEMU:) keyEquivalent: @""] autorelease]]; 12558524f1c7SJohn Arbuckle menuItem = [[[NSMenuItem alloc] initWithTitle: @"Resume" action: @selector(resumeQEMU:) keyEquivalent: @""] autorelease]; 12568524f1c7SJohn Arbuckle [menu addItem: menuItem]; 12578524f1c7SJohn Arbuckle [menuItem setEnabled: NO]; 125827074614SJohn Arbuckle [menu addItem: [NSMenuItem separatorItem]]; 125927074614SJohn Arbuckle [menu addItem: [[[NSMenuItem alloc] initWithTitle: @"Reset" action: @selector(restartQEMU:) keyEquivalent: @""] autorelease]]; 126027074614SJohn Arbuckle [menu addItem: [[[NSMenuItem alloc] initWithTitle: @"Power Down" action: @selector(powerDownQEMU:) keyEquivalent: @""] autorelease]]; 12618524f1c7SJohn Arbuckle menuItem = [[[NSMenuItem alloc] initWithTitle: @"Machine" action:nil keyEquivalent:@""] autorelease]; 12628524f1c7SJohn Arbuckle [menuItem setSubmenu:menu]; 12638524f1c7SJohn Arbuckle [[NSApp mainMenu] addItem:menuItem]; 12648524f1c7SJohn Arbuckle 12653e230dd2SCorentin Chary // View menu 12663e230dd2SCorentin Chary menu = [[NSMenu alloc] initWithTitle:@"View"]; 12675d1b2eefSProgrammingkid [menu addItem: [[[NSMenuItem alloc] initWithTitle:@"Enter Fullscreen" action:@selector(doToggleFullScreen:) keyEquivalent:@"f"] autorelease]]; // Fullscreen 12685d1b2eefSProgrammingkid [menu addItem: [[[NSMenuItem alloc] initWithTitle:@"Zoom To Fit" action:@selector(zoomToFit:) keyEquivalent:@""] autorelease]]; 12693e230dd2SCorentin Chary menuItem = [[[NSMenuItem alloc] initWithTitle:@"View" action:nil keyEquivalent:@""] autorelease]; 12703e230dd2SCorentin Chary [menuItem setSubmenu:menu]; 12713e230dd2SCorentin Chary [[NSApp mainMenu] addItem:menuItem]; 12723e230dd2SCorentin Chary 12733e230dd2SCorentin Chary // Window menu 12743e230dd2SCorentin Chary menu = [[NSMenu alloc] initWithTitle:@"Window"]; 12753e230dd2SCorentin Chary [menu addItem: [[[NSMenuItem alloc] initWithTitle:@"Minimize" action:@selector(performMiniaturize:) keyEquivalent:@"m"] autorelease]]; // Miniaturize 12763e230dd2SCorentin Chary menuItem = [[[NSMenuItem alloc] initWithTitle:@"Window" action:nil keyEquivalent:@""] autorelease]; 12773e230dd2SCorentin Chary [menuItem setSubmenu:menu]; 12783e230dd2SCorentin Chary [[NSApp mainMenu] addItem:menuItem]; 12793e230dd2SCorentin Chary [NSApp setWindowsMenu:menu]; 12803e230dd2SCorentin Chary 12813e230dd2SCorentin Chary // Help menu 12823e230dd2SCorentin Chary menu = [[NSMenu alloc] initWithTitle:@"Help"]; 12833e230dd2SCorentin Chary [menu addItem: [[[NSMenuItem alloc] initWithTitle:@"QEMU Documentation" action:@selector(showQEMUDoc:) keyEquivalent:@"?"] autorelease]]; // QEMU Help 12843e230dd2SCorentin Chary [menu addItem: [[[NSMenuItem alloc] initWithTitle:@"QEMU Technology" action:@selector(showQEMUTec:) keyEquivalent:@""] autorelease]]; // QEMU Help 12853e230dd2SCorentin Chary menuItem = [[[NSMenuItem alloc] initWithTitle:@"Window" action:nil keyEquivalent:@""] autorelease]; 12863e230dd2SCorentin Chary [menuItem setSubmenu:menu]; 12873e230dd2SCorentin Chary [[NSApp mainMenu] addItem:menuItem]; 12883e230dd2SCorentin Chary 12893e230dd2SCorentin Chary // Create an Application controller 12903e230dd2SCorentin Chary QemuCocoaAppController *appController = [[QemuCocoaAppController alloc] init]; 12913e230dd2SCorentin Chary [NSApp setDelegate:appController]; 12923e230dd2SCorentin Chary 12933e230dd2SCorentin Chary // Start the main event loop 12943e230dd2SCorentin Chary [NSApp run]; 12953e230dd2SCorentin Chary 12963e230dd2SCorentin Chary [appController release]; 12973e230dd2SCorentin Chary [pool release]; 12983e230dd2SCorentin Chary 12993e230dd2SCorentin Chary return 0; 13003e230dd2SCorentin Chary} 13013e230dd2SCorentin Chary 13023e230dd2SCorentin Chary 13033e230dd2SCorentin Chary 13043e230dd2SCorentin Chary#pragma mark qemu 13057c20b4a3SGerd Hoffmannstatic void cocoa_update(DisplayChangeListener *dcl, 13067c20b4a3SGerd Hoffmann int x, int y, int w, int h) 13073e230dd2SCorentin Chary{ 13086e657e64SPeter Maydell NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; 13096e657e64SPeter Maydell 13103e230dd2SCorentin Chary COCOA_DEBUG("qemu_cocoa: cocoa_update\n"); 13113e230dd2SCorentin Chary 13123e230dd2SCorentin Chary NSRect rect; 13133e230dd2SCorentin Chary if ([cocoaView cdx] == 1.0) { 13143e230dd2SCorentin Chary rect = NSMakeRect(x, [cocoaView gscreen].height - y - h, w, h); 13153e230dd2SCorentin Chary } else { 13163e230dd2SCorentin Chary rect = NSMakeRect( 13173e230dd2SCorentin Chary x * [cocoaView cdx], 13183e230dd2SCorentin Chary ([cocoaView gscreen].height - y - h) * [cocoaView cdy], 13193e230dd2SCorentin Chary w * [cocoaView cdx], 13203e230dd2SCorentin Chary h * [cocoaView cdy]); 13213e230dd2SCorentin Chary } 13223e230dd2SCorentin Chary [cocoaView setNeedsDisplayInRect:rect]; 13236e657e64SPeter Maydell 13246e657e64SPeter Maydell [pool release]; 13253e230dd2SCorentin Chary} 13263e230dd2SCorentin Chary 1327c12aeb86SGerd Hoffmannstatic void cocoa_switch(DisplayChangeListener *dcl, 1328c12aeb86SGerd Hoffmann DisplaySurface *surface) 13293e230dd2SCorentin Chary{ 13306e657e64SPeter Maydell NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; 13313e230dd2SCorentin Chary 13326e657e64SPeter Maydell COCOA_DEBUG("qemu_cocoa: cocoa_switch\n"); 13335e00d3acSGerd Hoffmann [cocoaView switchSurface:surface]; 13346e657e64SPeter Maydell [pool release]; 13353e230dd2SCorentin Chary} 13363e230dd2SCorentin Chary 1337bc2ed970SGerd Hoffmannstatic void cocoa_refresh(DisplayChangeListener *dcl) 13383e230dd2SCorentin Chary{ 13396e657e64SPeter Maydell NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; 13406e657e64SPeter Maydell 13413e230dd2SCorentin Chary COCOA_DEBUG("qemu_cocoa: cocoa_refresh\n"); 13423e230dd2SCorentin Chary 134321bae11aSGerd Hoffmann if (qemu_input_is_absolute()) { 13443e230dd2SCorentin Chary if (![cocoaView isAbsoluteEnabled]) { 134549b9bd4dSPeter Maydell if ([cocoaView isMouseGrabbed]) { 13463e230dd2SCorentin Chary [cocoaView ungrabMouse]; 13473e230dd2SCorentin Chary } 13483e230dd2SCorentin Chary } 13493e230dd2SCorentin Chary [cocoaView setAbsoluteEnabled:YES]; 13503e230dd2SCorentin Chary } 13513e230dd2SCorentin Chary 13523e230dd2SCorentin Chary NSDate *distantPast; 13533e230dd2SCorentin Chary NSEvent *event; 13543e230dd2SCorentin Chary distantPast = [NSDate distantPast]; 13553e230dd2SCorentin Chary do { 13563e230dd2SCorentin Chary event = [NSApp nextEventMatchingMask:NSAnyEventMask untilDate:distantPast 13573e230dd2SCorentin Chary inMode: NSDefaultRunLoopMode dequeue:YES]; 13583e230dd2SCorentin Chary if (event != nil) { 13593e230dd2SCorentin Chary [cocoaView handleEvent:event]; 13603e230dd2SCorentin Chary } 13613e230dd2SCorentin Chary } while(event != nil); 136268c0aa6eSPeter Maydell graphic_hw_update(NULL); 13636e657e64SPeter Maydell [pool release]; 13643e230dd2SCorentin Chary} 13653e230dd2SCorentin Chary 13663e230dd2SCorentin Charystatic void cocoa_cleanup(void) 13673e230dd2SCorentin Chary{ 13683e230dd2SCorentin Chary COCOA_DEBUG("qemu_cocoa: cocoa_cleanup\n"); 136958a06675SBlue Swirl g_free(dcl); 13703e230dd2SCorentin Chary} 13713e230dd2SCorentin Chary 13727c20b4a3SGerd Hoffmannstatic const DisplayChangeListenerOps dcl_ops = { 13737c20b4a3SGerd Hoffmann .dpy_name = "cocoa", 13748510d91eSPeter Maydell .dpy_gfx_update = cocoa_update, 13758510d91eSPeter Maydell .dpy_gfx_switch = cocoa_switch, 13768510d91eSPeter Maydell .dpy_refresh = cocoa_refresh, 13777c20b4a3SGerd Hoffmann}; 13787c20b4a3SGerd Hoffmann 1379b4c6a112SProgrammingkid/* Returns a name for a given console */ 1380b4c6a112SProgrammingkidstatic NSString * getConsoleName(QemuConsole * console) 1381b4c6a112SProgrammingkid{ 1382b4c6a112SProgrammingkid return [NSString stringWithFormat: @"%s", qemu_console_get_label(console)]; 1383b4c6a112SProgrammingkid} 1384b4c6a112SProgrammingkid 1385b4c6a112SProgrammingkid/* Add an entry to the View menu for each console */ 1386b4c6a112SProgrammingkidstatic void add_console_menu_entries(void) 1387b4c6a112SProgrammingkid{ 1388b4c6a112SProgrammingkid NSMenu *menu; 1389b4c6a112SProgrammingkid NSMenuItem *menuItem; 1390b4c6a112SProgrammingkid int index = 0; 1391b4c6a112SProgrammingkid 1392b4c6a112SProgrammingkid menu = [[[NSApp mainMenu] itemWithTitle:@"View"] submenu]; 1393b4c6a112SProgrammingkid 1394b4c6a112SProgrammingkid [menu addItem:[NSMenuItem separatorItem]]; 1395b4c6a112SProgrammingkid 1396b4c6a112SProgrammingkid while (qemu_console_lookup_by_index(index) != NULL) { 1397b4c6a112SProgrammingkid menuItem = [[[NSMenuItem alloc] initWithTitle: getConsoleName(qemu_console_lookup_by_index(index)) 1398b4c6a112SProgrammingkid action: @selector(displayConsole:) keyEquivalent: @""] autorelease]; 1399b4c6a112SProgrammingkid [menuItem setTag: index]; 1400b4c6a112SProgrammingkid [menu addItem: menuItem]; 1401b4c6a112SProgrammingkid index++; 1402b4c6a112SProgrammingkid } 1403b4c6a112SProgrammingkid} 1404b4c6a112SProgrammingkid 1405693a3e01SJohn Arbuckle/* Make menu items for all removable devices. 1406693a3e01SJohn Arbuckle * Each device is given an 'Eject' and 'Change' menu item. 1407693a3e01SJohn Arbuckle */ 1408693a3e01SJohn Arbucklestatic void addRemovableDevicesMenuItems() 1409693a3e01SJohn Arbuckle{ 1410693a3e01SJohn Arbuckle NSMenu *menu; 1411693a3e01SJohn Arbuckle NSMenuItem *menuItem; 1412693a3e01SJohn Arbuckle BlockInfoList *currentDevice, *pointerToFree; 1413693a3e01SJohn Arbuckle NSString *deviceName; 1414693a3e01SJohn Arbuckle 1415693a3e01SJohn Arbuckle currentDevice = qmp_query_block(NULL); 1416693a3e01SJohn Arbuckle pointerToFree = currentDevice; 1417693a3e01SJohn Arbuckle if(currentDevice == NULL) { 1418693a3e01SJohn Arbuckle NSBeep(); 1419693a3e01SJohn Arbuckle QEMU_Alert(@"Failed to query for block devices!"); 1420693a3e01SJohn Arbuckle return; 1421693a3e01SJohn Arbuckle } 1422693a3e01SJohn Arbuckle 1423693a3e01SJohn Arbuckle menu = [[[NSApp mainMenu] itemWithTitle:@"Machine"] submenu]; 1424693a3e01SJohn Arbuckle 1425693a3e01SJohn Arbuckle // Add a separator between related groups of menu items 1426693a3e01SJohn Arbuckle [menu addItem:[NSMenuItem separatorItem]]; 1427693a3e01SJohn Arbuckle 1428693a3e01SJohn Arbuckle // Set the attributes to the "Removable Media" menu item 1429693a3e01SJohn Arbuckle NSString *titleString = @"Removable Media"; 1430693a3e01SJohn Arbuckle NSMutableAttributedString *attString=[[NSMutableAttributedString alloc] initWithString:titleString]; 1431693a3e01SJohn Arbuckle NSColor *newColor = [NSColor blackColor]; 1432693a3e01SJohn Arbuckle NSFontManager *fontManager = [NSFontManager sharedFontManager]; 1433693a3e01SJohn Arbuckle NSFont *font = [fontManager fontWithFamily:@"Helvetica" 1434693a3e01SJohn Arbuckle traits:NSBoldFontMask|NSItalicFontMask 1435693a3e01SJohn Arbuckle weight:0 1436693a3e01SJohn Arbuckle size:14]; 1437693a3e01SJohn Arbuckle [attString addAttribute:NSFontAttributeName value:font range:NSMakeRange(0, [titleString length])]; 1438693a3e01SJohn Arbuckle [attString addAttribute:NSForegroundColorAttributeName value:newColor range:NSMakeRange(0, [titleString length])]; 1439693a3e01SJohn Arbuckle [attString addAttribute:NSUnderlineStyleAttributeName value:[NSNumber numberWithInt: 1] range:NSMakeRange(0, [titleString length])]; 1440693a3e01SJohn Arbuckle 1441693a3e01SJohn Arbuckle // Add the "Removable Media" menu item 1442693a3e01SJohn Arbuckle menuItem = [NSMenuItem new]; 1443693a3e01SJohn Arbuckle [menuItem setAttributedTitle: attString]; 1444693a3e01SJohn Arbuckle [menuItem setEnabled: NO]; 1445693a3e01SJohn Arbuckle [menu addItem: menuItem]; 1446693a3e01SJohn Arbuckle 1447693a3e01SJohn Arbuckle /* Loop thru all the block devices in the emulator */ 1448693a3e01SJohn Arbuckle while (currentDevice) { 1449693a3e01SJohn Arbuckle deviceName = [[NSString stringWithFormat: @"%s", currentDevice->value->device] retain]; 1450693a3e01SJohn Arbuckle 1451693a3e01SJohn Arbuckle if(currentDevice->value->removable) { 1452693a3e01SJohn Arbuckle menuItem = [[NSMenuItem alloc] initWithTitle: [NSString stringWithFormat: @"Change %s...", currentDevice->value->device] 1453693a3e01SJohn Arbuckle action: @selector(changeDeviceMedia:) 1454693a3e01SJohn Arbuckle keyEquivalent: @""]; 1455693a3e01SJohn Arbuckle [menu addItem: menuItem]; 1456693a3e01SJohn Arbuckle [menuItem setRepresentedObject: deviceName]; 1457693a3e01SJohn Arbuckle [menuItem autorelease]; 1458693a3e01SJohn Arbuckle 1459693a3e01SJohn Arbuckle menuItem = [[NSMenuItem alloc] initWithTitle: [NSString stringWithFormat: @"Eject %s", currentDevice->value->device] 1460693a3e01SJohn Arbuckle action: @selector(ejectDeviceMedia:) 1461693a3e01SJohn Arbuckle keyEquivalent: @""]; 1462693a3e01SJohn Arbuckle [menu addItem: menuItem]; 1463693a3e01SJohn Arbuckle [menuItem setRepresentedObject: deviceName]; 1464693a3e01SJohn Arbuckle [menuItem autorelease]; 1465693a3e01SJohn Arbuckle } 1466693a3e01SJohn Arbuckle currentDevice = currentDevice->next; 1467693a3e01SJohn Arbuckle } 1468693a3e01SJohn Arbuckle qapi_free_BlockInfoList(pointerToFree); 1469693a3e01SJohn Arbuckle} 1470693a3e01SJohn Arbuckle 14713e230dd2SCorentin Charyvoid cocoa_display_init(DisplayState *ds, int full_screen) 14723e230dd2SCorentin Chary{ 14733e230dd2SCorentin Chary COCOA_DEBUG("qemu_cocoa: cocoa_display_init\n"); 14743e230dd2SCorentin Chary 147543227af8SProgrammingkid /* if fullscreen mode is to be used */ 147643227af8SProgrammingkid if (full_screen == true) { 147743227af8SProgrammingkid [NSApp activateIgnoringOtherApps: YES]; 147843227af8SProgrammingkid [(QemuCocoaAppController *)[[NSApplication sharedApplication] delegate] toggleFullScreen: nil]; 147943227af8SProgrammingkid } 148043227af8SProgrammingkid 148158a06675SBlue Swirl dcl = g_malloc0(sizeof(DisplayChangeListener)); 14823e230dd2SCorentin Chary 14833e230dd2SCorentin Chary // register vga output callbacks 14847c20b4a3SGerd Hoffmann dcl->ops = &dcl_ops; 14855209089fSGerd Hoffmann register_displaychangelistener(dcl); 14863e230dd2SCorentin Chary 14873e230dd2SCorentin Chary // register cleanup function 14883e230dd2SCorentin Chary atexit(cocoa_cleanup); 1489b4c6a112SProgrammingkid 1490b4c6a112SProgrammingkid /* At this point QEMU has created all the consoles, so we can add View 1491b4c6a112SProgrammingkid * menu entries for them. 1492b4c6a112SProgrammingkid */ 1493b4c6a112SProgrammingkid add_console_menu_entries(); 1494693a3e01SJohn Arbuckle 1495693a3e01SJohn Arbuckle /* Give all removable devices a menu item. 1496693a3e01SJohn Arbuckle * Has to be called after QEMU has started to 1497693a3e01SJohn Arbuckle * find out what removable devices it has. 1498693a3e01SJohn Arbuckle */ 1499693a3e01SJohn Arbuckle addRemovableDevicesMenuItems(); 15003e230dd2SCorentin Chary} 1501