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 25e4a096b1SPeter Maydell#include "qemu/osdep.h" 26e4a096b1SPeter Maydell 273e230dd2SCorentin Chary#import <Cocoa/Cocoa.h> 283bbbee18SAndreas Färber#include <crt_externs.h> 293e230dd2SCorentin Chary 303e230dd2SCorentin Chary#include "qemu-common.h" 3128ecbaeeSPaolo Bonzini#include "ui/console.h" 3221bae11aSGerd Hoffmann#include "ui/input.h" 339c17d615SPaolo Bonzini#include "sysemu/sysemu.h" 348524f1c7SJohn Arbuckle#include "qmp-commands.h" 35693a3e01SJohn Arbuckle#include "sysemu/blockdev.h" 369e8204b1SProgrammingkid#include "qemu-version.h" 37aaac714fSJohn Arbuckle#include <Carbon/Carbon.h> 383e230dd2SCorentin Chary 393e230dd2SCorentin Chary#ifndef MAC_OS_X_VERSION_10_5 403e230dd2SCorentin Chary#define MAC_OS_X_VERSION_10_5 1050 413e230dd2SCorentin Chary#endif 422ba9de6eSPeter Maydell#ifndef MAC_OS_X_VERSION_10_6 432ba9de6eSPeter Maydell#define MAC_OS_X_VERSION_10_6 1060 442ba9de6eSPeter Maydell#endif 4581801ae2SPeter Maydell#ifndef MAC_OS_X_VERSION_10_10 4681801ae2SPeter Maydell#define MAC_OS_X_VERSION_10_10 101000 4781801ae2SPeter Maydell#endif 483e230dd2SCorentin Chary 493e230dd2SCorentin Chary 503e230dd2SCorentin Chary//#define DEBUG 513e230dd2SCorentin Chary 523e230dd2SCorentin Chary#ifdef DEBUG 533e230dd2SCorentin Chary#define COCOA_DEBUG(...) { (void) fprintf (stdout, __VA_ARGS__); } 543e230dd2SCorentin Chary#else 553e230dd2SCorentin Chary#define COCOA_DEBUG(...) ((void) 0) 563e230dd2SCorentin Chary#endif 573e230dd2SCorentin Chary 583e230dd2SCorentin Chary#define cgrect(nsrect) (*(CGRect *)&(nsrect)) 593e230dd2SCorentin Chary 603e230dd2SCorentin Charytypedef struct { 613e230dd2SCorentin Chary int width; 623e230dd2SCorentin Chary int height; 633e230dd2SCorentin Chary int bitsPerComponent; 643e230dd2SCorentin Chary int bitsPerPixel; 653e230dd2SCorentin Chary} QEMUScreen; 663e230dd2SCorentin Chary 679e8204b1SProgrammingkidNSWindow *normalWindow, *about_window; 683e230dd2SCorentin Charystatic DisplayChangeListener *dcl; 6921bae11aSGerd Hoffmannstatic int last_buttons; 703e230dd2SCorentin Chary 713e230dd2SCorentin Charyint gArgc; 723e230dd2SCorentin Charychar **gArgv; 735d1b2eefSProgrammingkidbool stretch_video; 748524f1c7SJohn ArbuckleNSTextField *pauseLabel; 75693a3e01SJohn ArbuckleNSArray * supportedImageFileTypes; 763e230dd2SCorentin Chary 77aaac714fSJohn Arbuckle// Mac to QKeyCode conversion 78aaac714fSJohn Arbuckleconst int mac_to_qkeycode_map[] = { 79aaac714fSJohn Arbuckle [kVK_ANSI_A] = Q_KEY_CODE_A, 80aaac714fSJohn Arbuckle [kVK_ANSI_B] = Q_KEY_CODE_B, 81aaac714fSJohn Arbuckle [kVK_ANSI_C] = Q_KEY_CODE_C, 82aaac714fSJohn Arbuckle [kVK_ANSI_D] = Q_KEY_CODE_D, 83aaac714fSJohn Arbuckle [kVK_ANSI_E] = Q_KEY_CODE_E, 84aaac714fSJohn Arbuckle [kVK_ANSI_F] = Q_KEY_CODE_F, 85aaac714fSJohn Arbuckle [kVK_ANSI_G] = Q_KEY_CODE_G, 86aaac714fSJohn Arbuckle [kVK_ANSI_H] = Q_KEY_CODE_H, 87aaac714fSJohn Arbuckle [kVK_ANSI_I] = Q_KEY_CODE_I, 88aaac714fSJohn Arbuckle [kVK_ANSI_J] = Q_KEY_CODE_J, 89aaac714fSJohn Arbuckle [kVK_ANSI_K] = Q_KEY_CODE_K, 90aaac714fSJohn Arbuckle [kVK_ANSI_L] = Q_KEY_CODE_L, 91aaac714fSJohn Arbuckle [kVK_ANSI_M] = Q_KEY_CODE_M, 92aaac714fSJohn Arbuckle [kVK_ANSI_N] = Q_KEY_CODE_N, 93aaac714fSJohn Arbuckle [kVK_ANSI_O] = Q_KEY_CODE_O, 94aaac714fSJohn Arbuckle [kVK_ANSI_P] = Q_KEY_CODE_P, 95aaac714fSJohn Arbuckle [kVK_ANSI_Q] = Q_KEY_CODE_Q, 96aaac714fSJohn Arbuckle [kVK_ANSI_R] = Q_KEY_CODE_R, 97aaac714fSJohn Arbuckle [kVK_ANSI_S] = Q_KEY_CODE_S, 98aaac714fSJohn Arbuckle [kVK_ANSI_T] = Q_KEY_CODE_T, 99aaac714fSJohn Arbuckle [kVK_ANSI_U] = Q_KEY_CODE_U, 100aaac714fSJohn Arbuckle [kVK_ANSI_V] = Q_KEY_CODE_V, 101aaac714fSJohn Arbuckle [kVK_ANSI_W] = Q_KEY_CODE_W, 102aaac714fSJohn Arbuckle [kVK_ANSI_X] = Q_KEY_CODE_X, 103aaac714fSJohn Arbuckle [kVK_ANSI_Y] = Q_KEY_CODE_Y, 104aaac714fSJohn Arbuckle [kVK_ANSI_Z] = Q_KEY_CODE_Z, 1053e230dd2SCorentin Chary 106aaac714fSJohn Arbuckle [kVK_ANSI_0] = Q_KEY_CODE_0, 107aaac714fSJohn Arbuckle [kVK_ANSI_1] = Q_KEY_CODE_1, 108aaac714fSJohn Arbuckle [kVK_ANSI_2] = Q_KEY_CODE_2, 109aaac714fSJohn Arbuckle [kVK_ANSI_3] = Q_KEY_CODE_3, 110aaac714fSJohn Arbuckle [kVK_ANSI_4] = Q_KEY_CODE_4, 111aaac714fSJohn Arbuckle [kVK_ANSI_5] = Q_KEY_CODE_5, 112aaac714fSJohn Arbuckle [kVK_ANSI_6] = Q_KEY_CODE_6, 113aaac714fSJohn Arbuckle [kVK_ANSI_7] = Q_KEY_CODE_7, 114aaac714fSJohn Arbuckle [kVK_ANSI_8] = Q_KEY_CODE_8, 115aaac714fSJohn Arbuckle [kVK_ANSI_9] = Q_KEY_CODE_9, 116aaac714fSJohn Arbuckle 117aaac714fSJohn Arbuckle [kVK_ANSI_Grave] = Q_KEY_CODE_GRAVE_ACCENT, 118aaac714fSJohn Arbuckle [kVK_ANSI_Minus] = Q_KEY_CODE_MINUS, 119aaac714fSJohn Arbuckle [kVK_ANSI_Equal] = Q_KEY_CODE_EQUAL, 120aaac714fSJohn Arbuckle [kVK_Delete] = Q_KEY_CODE_BACKSPACE, 121aaac714fSJohn Arbuckle [kVK_CapsLock] = Q_KEY_CODE_CAPS_LOCK, 122aaac714fSJohn Arbuckle [kVK_Tab] = Q_KEY_CODE_TAB, 123aaac714fSJohn Arbuckle [kVK_Return] = Q_KEY_CODE_RET, 124aaac714fSJohn Arbuckle [kVK_ANSI_LeftBracket] = Q_KEY_CODE_BRACKET_LEFT, 125aaac714fSJohn Arbuckle [kVK_ANSI_RightBracket] = Q_KEY_CODE_BRACKET_RIGHT, 126aaac714fSJohn Arbuckle [kVK_ANSI_Backslash] = Q_KEY_CODE_BACKSLASH, 127aaac714fSJohn Arbuckle [kVK_ANSI_Semicolon] = Q_KEY_CODE_SEMICOLON, 128aaac714fSJohn Arbuckle [kVK_ANSI_Quote] = Q_KEY_CODE_APOSTROPHE, 129aaac714fSJohn Arbuckle [kVK_ANSI_Comma] = Q_KEY_CODE_COMMA, 130aaac714fSJohn Arbuckle [kVK_ANSI_Period] = Q_KEY_CODE_DOT, 131aaac714fSJohn Arbuckle [kVK_ANSI_Slash] = Q_KEY_CODE_SLASH, 132aaac714fSJohn Arbuckle [kVK_Shift] = Q_KEY_CODE_SHIFT, 133aaac714fSJohn Arbuckle [kVK_RightShift] = Q_KEY_CODE_SHIFT_R, 134aaac714fSJohn Arbuckle [kVK_Control] = Q_KEY_CODE_CTRL, 135aaac714fSJohn Arbuckle [kVK_RightControl] = Q_KEY_CODE_CTRL_R, 136aaac714fSJohn Arbuckle [kVK_Option] = Q_KEY_CODE_ALT, 137aaac714fSJohn Arbuckle [kVK_RightOption] = Q_KEY_CODE_ALT_R, 138aaac714fSJohn Arbuckle [kVK_Command] = Q_KEY_CODE_META_L, 139aaac714fSJohn Arbuckle [0x36] = Q_KEY_CODE_META_R, /* There is no kVK_RightCommand */ 140aaac714fSJohn Arbuckle [kVK_Space] = Q_KEY_CODE_SPC, 141aaac714fSJohn Arbuckle 142aaac714fSJohn Arbuckle [kVK_ANSI_Keypad0] = Q_KEY_CODE_KP_0, 143aaac714fSJohn Arbuckle [kVK_ANSI_Keypad1] = Q_KEY_CODE_KP_1, 144aaac714fSJohn Arbuckle [kVK_ANSI_Keypad2] = Q_KEY_CODE_KP_2, 145aaac714fSJohn Arbuckle [kVK_ANSI_Keypad3] = Q_KEY_CODE_KP_3, 146aaac714fSJohn Arbuckle [kVK_ANSI_Keypad4] = Q_KEY_CODE_KP_4, 147aaac714fSJohn Arbuckle [kVK_ANSI_Keypad5] = Q_KEY_CODE_KP_5, 148aaac714fSJohn Arbuckle [kVK_ANSI_Keypad6] = Q_KEY_CODE_KP_6, 149aaac714fSJohn Arbuckle [kVK_ANSI_Keypad7] = Q_KEY_CODE_KP_7, 150aaac714fSJohn Arbuckle [kVK_ANSI_Keypad8] = Q_KEY_CODE_KP_8, 151aaac714fSJohn Arbuckle [kVK_ANSI_Keypad9] = Q_KEY_CODE_KP_9, 152aaac714fSJohn Arbuckle [kVK_ANSI_KeypadDecimal] = Q_KEY_CODE_KP_DECIMAL, 153aaac714fSJohn Arbuckle [kVK_ANSI_KeypadEnter] = Q_KEY_CODE_KP_ENTER, 154aaac714fSJohn Arbuckle [kVK_ANSI_KeypadPlus] = Q_KEY_CODE_KP_ADD, 155aaac714fSJohn Arbuckle [kVK_ANSI_KeypadMinus] = Q_KEY_CODE_KP_SUBTRACT, 156aaac714fSJohn Arbuckle [kVK_ANSI_KeypadMultiply] = Q_KEY_CODE_KP_MULTIPLY, 157aaac714fSJohn Arbuckle [kVK_ANSI_KeypadDivide] = Q_KEY_CODE_KP_DIVIDE, 158aaac714fSJohn Arbuckle [kVK_ANSI_KeypadEquals] = Q_KEY_CODE_KP_EQUALS, 159aaac714fSJohn Arbuckle [kVK_ANSI_KeypadClear] = Q_KEY_CODE_NUM_LOCK, 160aaac714fSJohn Arbuckle 161aaac714fSJohn Arbuckle [kVK_UpArrow] = Q_KEY_CODE_UP, 162aaac714fSJohn Arbuckle [kVK_DownArrow] = Q_KEY_CODE_DOWN, 163aaac714fSJohn Arbuckle [kVK_LeftArrow] = Q_KEY_CODE_LEFT, 164aaac714fSJohn Arbuckle [kVK_RightArrow] = Q_KEY_CODE_RIGHT, 165aaac714fSJohn Arbuckle 166aaac714fSJohn Arbuckle [kVK_Help] = Q_KEY_CODE_INSERT, 167aaac714fSJohn Arbuckle [kVK_Home] = Q_KEY_CODE_HOME, 168aaac714fSJohn Arbuckle [kVK_PageUp] = Q_KEY_CODE_PGUP, 169aaac714fSJohn Arbuckle [kVK_PageDown] = Q_KEY_CODE_PGDN, 170aaac714fSJohn Arbuckle [kVK_End] = Q_KEY_CODE_END, 171aaac714fSJohn Arbuckle [kVK_ForwardDelete] = Q_KEY_CODE_DELETE, 172aaac714fSJohn Arbuckle 173aaac714fSJohn Arbuckle [kVK_Escape] = Q_KEY_CODE_ESC, 174aaac714fSJohn Arbuckle 175aaac714fSJohn Arbuckle /* The Power key can't be used directly because the operating system uses 176aaac714fSJohn Arbuckle * it. This key can be emulated by using it in place of another key such as 177aaac714fSJohn Arbuckle * F1. Don't forget to disable the real key binding. 178aaac714fSJohn Arbuckle */ 179aaac714fSJohn Arbuckle /* [kVK_F1] = Q_KEY_CODE_POWER, */ 180aaac714fSJohn Arbuckle 181aaac714fSJohn Arbuckle [kVK_F1] = Q_KEY_CODE_F1, 182aaac714fSJohn Arbuckle [kVK_F2] = Q_KEY_CODE_F2, 183aaac714fSJohn Arbuckle [kVK_F3] = Q_KEY_CODE_F3, 184aaac714fSJohn Arbuckle [kVK_F4] = Q_KEY_CODE_F4, 185aaac714fSJohn Arbuckle [kVK_F5] = Q_KEY_CODE_F5, 186aaac714fSJohn Arbuckle [kVK_F6] = Q_KEY_CODE_F6, 187aaac714fSJohn Arbuckle [kVK_F7] = Q_KEY_CODE_F7, 188aaac714fSJohn Arbuckle [kVK_F8] = Q_KEY_CODE_F8, 189aaac714fSJohn Arbuckle [kVK_F9] = Q_KEY_CODE_F9, 190aaac714fSJohn Arbuckle [kVK_F10] = Q_KEY_CODE_F10, 191aaac714fSJohn Arbuckle [kVK_F11] = Q_KEY_CODE_F11, 192aaac714fSJohn Arbuckle [kVK_F12] = Q_KEY_CODE_F12, 193aaac714fSJohn Arbuckle [kVK_F13] = Q_KEY_CODE_PRINT, 194aaac714fSJohn Arbuckle [kVK_F14] = Q_KEY_CODE_SCROLL_LOCK, 195aaac714fSJohn Arbuckle [kVK_F15] = Q_KEY_CODE_PAUSE, 196aaac714fSJohn Arbuckle 1973e230dd2SCorentin Chary /* 198aaac714fSJohn Arbuckle * The eject and volume keys can't be used here because they are handled at 199aaac714fSJohn Arbuckle * a lower level than what an Application can see. 2003e230dd2SCorentin Chary */ 2013e230dd2SCorentin Chary}; 2023e230dd2SCorentin Chary 2033e230dd2SCorentin Charystatic int cocoa_keycode_to_qemu(int keycode) 2043e230dd2SCorentin Chary{ 205aaac714fSJohn Arbuckle if (ARRAY_SIZE(mac_to_qkeycode_map) <= keycode) { 20601cc4e6fSPeter Maydell fprintf(stderr, "(cocoa) warning unknown keycode 0x%x\n", keycode); 2073e230dd2SCorentin Chary return 0; 2083e230dd2SCorentin Chary } 209aaac714fSJohn Arbuckle return mac_to_qkeycode_map[keycode]; 2103e230dd2SCorentin Chary} 2113e230dd2SCorentin Chary 212693a3e01SJohn Arbuckle/* Displays an alert dialog box with the specified message */ 213693a3e01SJohn Arbucklestatic void QEMU_Alert(NSString *message) 214693a3e01SJohn Arbuckle{ 215693a3e01SJohn Arbuckle NSAlert *alert; 216693a3e01SJohn Arbuckle alert = [NSAlert new]; 217693a3e01SJohn Arbuckle [alert setMessageText: message]; 218693a3e01SJohn Arbuckle [alert runModal]; 219693a3e01SJohn Arbuckle} 2203e230dd2SCorentin Chary 221693a3e01SJohn Arbuckle/* Handles any errors that happen with a device transaction */ 222693a3e01SJohn Arbucklestatic void handleAnyDeviceErrors(Error * err) 223693a3e01SJohn Arbuckle{ 224693a3e01SJohn Arbuckle if (err) { 225693a3e01SJohn Arbuckle QEMU_Alert([NSString stringWithCString: error_get_pretty(err) 226693a3e01SJohn Arbuckle encoding: NSASCIIStringEncoding]); 227693a3e01SJohn Arbuckle error_free(err); 228693a3e01SJohn Arbuckle } 229693a3e01SJohn Arbuckle} 2303e230dd2SCorentin Chary 2313e230dd2SCorentin Chary/* 2323e230dd2SCorentin Chary ------------------------------------------------------ 2333e230dd2SCorentin Chary QemuCocoaView 2343e230dd2SCorentin Chary ------------------------------------------------------ 2353e230dd2SCorentin Chary*/ 2363e230dd2SCorentin Chary@interface QemuCocoaView : NSView 2373e230dd2SCorentin Chary{ 2383e230dd2SCorentin Chary QEMUScreen screen; 2393e230dd2SCorentin Chary NSWindow *fullScreenWindow; 2403e230dd2SCorentin Chary float cx,cy,cw,ch,cdx,cdy; 2413e230dd2SCorentin Chary CGDataProviderRef dataProviderRef; 2423e230dd2SCorentin Chary int modifiers_state[256]; 24349b9bd4dSPeter Maydell BOOL isMouseGrabbed; 2443e230dd2SCorentin Chary BOOL isFullscreen; 2453e230dd2SCorentin Chary BOOL isAbsoluteEnabled; 246f61c387eSPeter Maydell BOOL isMouseDeassociated; 2473e230dd2SCorentin Chary} 2485e00d3acSGerd Hoffmann- (void) switchSurface:(DisplaySurface *)surface; 2493e230dd2SCorentin Chary- (void) grabMouse; 2503e230dd2SCorentin Chary- (void) ungrabMouse; 2513e230dd2SCorentin Chary- (void) toggleFullScreen:(id)sender; 2523e230dd2SCorentin Chary- (void) handleEvent:(NSEvent *)event; 2533e230dd2SCorentin Chary- (void) setAbsoluteEnabled:(BOOL)tIsAbsoluteEnabled; 254f61c387eSPeter Maydell/* The state surrounding mouse grabbing is potentially confusing. 255f61c387eSPeter Maydell * isAbsoluteEnabled tracks qemu_input_is_absolute() [ie "is the emulated 256f61c387eSPeter Maydell * pointing device an absolute-position one?"], but is only updated on 257f61c387eSPeter Maydell * next refresh. 258f61c387eSPeter Maydell * isMouseGrabbed tracks whether GUI events are directed to the guest; 259f61c387eSPeter Maydell * it controls whether special keys like Cmd get sent to the guest, 260f61c387eSPeter Maydell * and whether we capture the mouse when in non-absolute mode. 261f61c387eSPeter Maydell * isMouseDeassociated tracks whether we've told MacOSX to disassociate 262f61c387eSPeter Maydell * the mouse and mouse cursor position by calling 263f61c387eSPeter Maydell * CGAssociateMouseAndMouseCursorPosition(FALSE) 264f61c387eSPeter Maydell * (which basically happens if we grab in non-absolute mode). 265f61c387eSPeter Maydell */ 26649b9bd4dSPeter Maydell- (BOOL) isMouseGrabbed; 2673e230dd2SCorentin Chary- (BOOL) isAbsoluteEnabled; 268f61c387eSPeter Maydell- (BOOL) isMouseDeassociated; 2693e230dd2SCorentin Chary- (float) cdx; 2703e230dd2SCorentin Chary- (float) cdy; 2713e230dd2SCorentin Chary- (QEMUScreen) gscreen; 2723b178b71SJohn Arbuckle- (void) raiseAllKeys; 2733e230dd2SCorentin Chary@end 2743e230dd2SCorentin Chary 2757fee199cSAndreas FärberQemuCocoaView *cocoaView; 2767fee199cSAndreas Färber 2773e230dd2SCorentin Chary@implementation QemuCocoaView 2783e230dd2SCorentin Chary- (id)initWithFrame:(NSRect)frameRect 2793e230dd2SCorentin Chary{ 2803e230dd2SCorentin Chary COCOA_DEBUG("QemuCocoaView: initWithFrame\n"); 2813e230dd2SCorentin Chary 2823e230dd2SCorentin Chary self = [super initWithFrame:frameRect]; 2833e230dd2SCorentin Chary if (self) { 2843e230dd2SCorentin Chary 2853e230dd2SCorentin Chary screen.bitsPerComponent = 8; 2863e230dd2SCorentin Chary screen.bitsPerPixel = 32; 2873e230dd2SCorentin Chary screen.width = frameRect.size.width; 2883e230dd2SCorentin Chary screen.height = frameRect.size.height; 2893e230dd2SCorentin Chary 2903e230dd2SCorentin Chary } 2913e230dd2SCorentin Chary return self; 2923e230dd2SCorentin Chary} 2933e230dd2SCorentin Chary 2943e230dd2SCorentin Chary- (void) dealloc 2953e230dd2SCorentin Chary{ 2963e230dd2SCorentin Chary COCOA_DEBUG("QemuCocoaView: dealloc\n"); 2973e230dd2SCorentin Chary 2983e230dd2SCorentin Chary if (dataProviderRef) 2993e230dd2SCorentin Chary CGDataProviderRelease(dataProviderRef); 3003e230dd2SCorentin Chary 3013e230dd2SCorentin Chary [super dealloc]; 3023e230dd2SCorentin Chary} 3033e230dd2SCorentin Chary 3043e230dd2SCorentin Chary- (BOOL) isOpaque 3053e230dd2SCorentin Chary{ 3063e230dd2SCorentin Chary return YES; 3073e230dd2SCorentin Chary} 3083e230dd2SCorentin Chary 3095dd45beeSPeter Maydell- (BOOL) screenContainsPoint:(NSPoint) p 3105dd45beeSPeter Maydell{ 3115dd45beeSPeter Maydell return (p.x > -1 && p.x < screen.width && p.y > -1 && p.y < screen.height); 3125dd45beeSPeter Maydell} 3135dd45beeSPeter Maydell 31413aefd30SPeter Maydell- (void) hideCursor 31513aefd30SPeter Maydell{ 31613aefd30SPeter Maydell if (!cursor_hide) { 31713aefd30SPeter Maydell return; 31813aefd30SPeter Maydell } 31913aefd30SPeter Maydell [NSCursor hide]; 32013aefd30SPeter Maydell} 32113aefd30SPeter Maydell 32213aefd30SPeter Maydell- (void) unhideCursor 32313aefd30SPeter Maydell{ 32413aefd30SPeter Maydell if (!cursor_hide) { 32513aefd30SPeter Maydell return; 32613aefd30SPeter Maydell } 32713aefd30SPeter Maydell [NSCursor unhide]; 32813aefd30SPeter Maydell} 32913aefd30SPeter Maydell 3303e230dd2SCorentin Chary- (void) drawRect:(NSRect) rect 3313e230dd2SCorentin Chary{ 3323e230dd2SCorentin Chary COCOA_DEBUG("QemuCocoaView: drawRect\n"); 3333e230dd2SCorentin Chary 3343e230dd2SCorentin Chary // get CoreGraphic context 3353e230dd2SCorentin Chary CGContextRef viewContextRef = [[NSGraphicsContext currentContext] graphicsPort]; 3363e230dd2SCorentin Chary CGContextSetInterpolationQuality (viewContextRef, kCGInterpolationNone); 3373e230dd2SCorentin Chary CGContextSetShouldAntialias (viewContextRef, NO); 3383e230dd2SCorentin Chary 3393e230dd2SCorentin Chary // draw screen bitmap directly to Core Graphics context 3407d270b1cSPeter Maydell if (!dataProviderRef) { 3417d270b1cSPeter Maydell // Draw request before any guest device has set up a framebuffer: 3427d270b1cSPeter Maydell // just draw an opaque black rectangle 3437d270b1cSPeter Maydell CGContextSetRGBFillColor(viewContextRef, 0, 0, 0, 1.0); 3447d270b1cSPeter Maydell CGContextFillRect(viewContextRef, NSRectToCGRect(rect)); 3457d270b1cSPeter Maydell } else { 3463e230dd2SCorentin Chary CGImageRef imageRef = CGImageCreate( 3473e230dd2SCorentin Chary screen.width, //width 3483e230dd2SCorentin Chary screen.height, //height 3493e230dd2SCorentin Chary screen.bitsPerComponent, //bitsPerComponent 3503e230dd2SCorentin Chary screen.bitsPerPixel, //bitsPerPixel 3513e230dd2SCorentin Chary (screen.width * (screen.bitsPerComponent/2)), //bytesPerRow 3523e230dd2SCorentin Chary#ifdef __LITTLE_ENDIAN__ 3533e230dd2SCorentin Chary CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB), //colorspace for OS X >= 10.4 3543e230dd2SCorentin Chary kCGBitmapByteOrder32Little | kCGImageAlphaNoneSkipFirst, 3553e230dd2SCorentin Chary#else 3563e230dd2SCorentin Chary CGColorSpaceCreateDeviceRGB(), //colorspace for OS X < 10.4 (actually ppc) 3573e230dd2SCorentin Chary kCGImageAlphaNoneSkipFirst, //bitmapInfo 3583e230dd2SCorentin Chary#endif 3593e230dd2SCorentin Chary dataProviderRef, //provider 3603e230dd2SCorentin Chary NULL, //decode 3613e230dd2SCorentin Chary 0, //interpolate 3623e230dd2SCorentin Chary kCGRenderingIntentDefault //intent 3633e230dd2SCorentin Chary ); 3643e230dd2SCorentin Chary // selective drawing code (draws only dirty rectangles) (OS X >= 10.4) 3653e230dd2SCorentin Chary const NSRect *rectList; 3663e230dd2SCorentin Chary NSInteger rectCount; 3673e230dd2SCorentin Chary int i; 3683e230dd2SCorentin Chary CGImageRef clipImageRef; 3693e230dd2SCorentin Chary CGRect clipRect; 3703e230dd2SCorentin Chary 3713e230dd2SCorentin Chary [self getRectsBeingDrawn:&rectList count:&rectCount]; 3723e230dd2SCorentin Chary for (i = 0; i < rectCount; i++) { 3733e230dd2SCorentin Chary clipRect.origin.x = rectList[i].origin.x / cdx; 3743e230dd2SCorentin Chary clipRect.origin.y = (float)screen.height - (rectList[i].origin.y + rectList[i].size.height) / cdy; 3753e230dd2SCorentin Chary clipRect.size.width = rectList[i].size.width / cdx; 3763e230dd2SCorentin Chary clipRect.size.height = rectList[i].size.height / cdy; 3773e230dd2SCorentin Chary clipImageRef = CGImageCreateWithImageInRect( 3783e230dd2SCorentin Chary imageRef, 3793e230dd2SCorentin Chary clipRect 3803e230dd2SCorentin Chary ); 3813e230dd2SCorentin Chary CGContextDrawImage (viewContextRef, cgrect(rectList[i]), clipImageRef); 3823e230dd2SCorentin Chary CGImageRelease (clipImageRef); 3833e230dd2SCorentin Chary } 3843e230dd2SCorentin Chary CGImageRelease (imageRef); 3853e230dd2SCorentin Chary } 3863e230dd2SCorentin Chary} 3873e230dd2SCorentin Chary 3883e230dd2SCorentin Chary- (void) setContentDimensions 3893e230dd2SCorentin Chary{ 3903e230dd2SCorentin Chary COCOA_DEBUG("QemuCocoaView: setContentDimensions\n"); 3913e230dd2SCorentin Chary 3923e230dd2SCorentin Chary if (isFullscreen) { 3933e230dd2SCorentin Chary cdx = [[NSScreen mainScreen] frame].size.width / (float)screen.width; 3943e230dd2SCorentin Chary cdy = [[NSScreen mainScreen] frame].size.height / (float)screen.height; 3955d1b2eefSProgrammingkid 3965d1b2eefSProgrammingkid /* stretches video, but keeps same aspect ratio */ 3975d1b2eefSProgrammingkid if (stretch_video == true) { 3985d1b2eefSProgrammingkid /* use smallest stretch value - prevents clipping on sides */ 3995d1b2eefSProgrammingkid if (MIN(cdx, cdy) == cdx) { 4005d1b2eefSProgrammingkid cdy = cdx; 4015d1b2eefSProgrammingkid } else { 4025d1b2eefSProgrammingkid cdx = cdy; 4035d1b2eefSProgrammingkid } 4045d1b2eefSProgrammingkid } else { /* No stretching */ 4055d1b2eefSProgrammingkid cdx = cdy = 1; 4065d1b2eefSProgrammingkid } 4073e230dd2SCorentin Chary cw = screen.width * cdx; 4083e230dd2SCorentin Chary ch = screen.height * cdy; 4093e230dd2SCorentin Chary cx = ([[NSScreen mainScreen] frame].size.width - cw) / 2.0; 4103e230dd2SCorentin Chary cy = ([[NSScreen mainScreen] frame].size.height - ch) / 2.0; 4113e230dd2SCorentin Chary } else { 4123e230dd2SCorentin Chary cx = 0; 4133e230dd2SCorentin Chary cy = 0; 4143e230dd2SCorentin Chary cw = screen.width; 4153e230dd2SCorentin Chary ch = screen.height; 4163e230dd2SCorentin Chary cdx = 1.0; 4173e230dd2SCorentin Chary cdy = 1.0; 4183e230dd2SCorentin Chary } 4193e230dd2SCorentin Chary} 4203e230dd2SCorentin Chary 4215e00d3acSGerd Hoffmann- (void) switchSurface:(DisplaySurface *)surface 4223e230dd2SCorentin Chary{ 4235e00d3acSGerd Hoffmann COCOA_DEBUG("QemuCocoaView: switchSurface\n"); 4243e230dd2SCorentin Chary 4258510d91eSPeter Maydell int w = surface_width(surface); 4268510d91eSPeter Maydell int h = surface_height(surface); 427381600daSPeter Maydell /* cdx == 0 means this is our very first surface, in which case we need 428381600daSPeter Maydell * to recalculate the content dimensions even if it happens to be the size 429381600daSPeter Maydell * of the initial empty window. 430381600daSPeter Maydell */ 431381600daSPeter Maydell bool isResize = (w != screen.width || h != screen.height || cdx == 0.0); 432d3345a04SPeter Maydell 433d3345a04SPeter Maydell int oldh = screen.height; 434d3345a04SPeter Maydell if (isResize) { 435d3345a04SPeter Maydell // Resize before we trigger the redraw, or we'll redraw at the wrong size 436d3345a04SPeter Maydell COCOA_DEBUG("switchSurface: new size %d x %d\n", w, h); 437d3345a04SPeter Maydell screen.width = w; 438d3345a04SPeter Maydell screen.height = h; 439d3345a04SPeter Maydell [self setContentDimensions]; 440d3345a04SPeter Maydell [self setFrame:NSMakeRect(cx, cy, cw, ch)]; 441d3345a04SPeter Maydell } 4428510d91eSPeter Maydell 4433e230dd2SCorentin Chary // update screenBuffer 4443e230dd2SCorentin Chary if (dataProviderRef) 4453e230dd2SCorentin Chary CGDataProviderRelease(dataProviderRef); 4463e230dd2SCorentin Chary 4473e230dd2SCorentin Chary //sync host window color space with guests 4485e00d3acSGerd Hoffmann screen.bitsPerPixel = surface_bits_per_pixel(surface); 4495e00d3acSGerd Hoffmann screen.bitsPerComponent = surface_bytes_per_pixel(surface) * 2; 4503e230dd2SCorentin Chary 4515e00d3acSGerd Hoffmann dataProviderRef = CGDataProviderCreateWithData(NULL, surface_data(surface), w * 4 * h, NULL); 4523e230dd2SCorentin Chary 4533e230dd2SCorentin Chary // update windows 4543e230dd2SCorentin Chary if (isFullscreen) { 4553e230dd2SCorentin Chary [[fullScreenWindow contentView] setFrame:[[NSScreen mainScreen] frame]]; 456d3345a04SPeter 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]; 4573e230dd2SCorentin Chary } else { 4583e230dd2SCorentin Chary if (qemu_name) 4593e230dd2SCorentin Chary [normalWindow setTitle:[NSString stringWithFormat:@"QEMU %s", qemu_name]]; 460d3345a04SPeter 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]; 4613e230dd2SCorentin Chary } 462d3345a04SPeter Maydell 463d3345a04SPeter Maydell if (isResize) { 4643e230dd2SCorentin Chary [normalWindow center]; 465d3345a04SPeter Maydell } 4663e230dd2SCorentin Chary} 4673e230dd2SCorentin Chary 4683e230dd2SCorentin Chary- (void) toggleFullScreen:(id)sender 4693e230dd2SCorentin Chary{ 4703e230dd2SCorentin Chary COCOA_DEBUG("QemuCocoaView: toggleFullScreen\n"); 4713e230dd2SCorentin Chary 4723e230dd2SCorentin Chary if (isFullscreen) { // switch from fullscreen to desktop 4733e230dd2SCorentin Chary isFullscreen = FALSE; 4743e230dd2SCorentin Chary [self ungrabMouse]; 4753e230dd2SCorentin Chary [self setContentDimensions]; 4763e230dd2SCorentin Chary if ([NSView respondsToSelector:@selector(exitFullScreenModeWithOptions:)]) { // test if "exitFullScreenModeWithOptions" is supported on host at runtime 4773e230dd2SCorentin Chary [self exitFullScreenModeWithOptions:nil]; 4783e230dd2SCorentin Chary } else { 4793e230dd2SCorentin Chary [fullScreenWindow close]; 4803e230dd2SCorentin Chary [normalWindow setContentView: self]; 4813e230dd2SCorentin Chary [normalWindow makeKeyAndOrderFront: self]; 4823e230dd2SCorentin Chary [NSMenu setMenuBarVisible:YES]; 4833e230dd2SCorentin Chary } 4843e230dd2SCorentin Chary } else { // switch from desktop to fullscreen 4853e230dd2SCorentin Chary isFullscreen = TRUE; 4865d1b2eefSProgrammingkid [normalWindow orderOut: nil]; /* Hide the window */ 4873e230dd2SCorentin Chary [self grabMouse]; 4883e230dd2SCorentin Chary [self setContentDimensions]; 4893e230dd2SCorentin Chary if ([NSView respondsToSelector:@selector(enterFullScreenMode:withOptions:)]) { // test if "enterFullScreenMode:withOptions" is supported on host at runtime 4903e230dd2SCorentin Chary [self enterFullScreenMode:[NSScreen mainScreen] withOptions:[NSDictionary dictionaryWithObjectsAndKeys: 4913e230dd2SCorentin Chary [NSNumber numberWithBool:NO], NSFullScreenModeAllScreens, 4923e230dd2SCorentin Chary [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:NO], kCGDisplayModeIsStretched, nil], NSFullScreenModeSetting, 4933e230dd2SCorentin Chary nil]]; 4943e230dd2SCorentin Chary } else { 4953e230dd2SCorentin Chary [NSMenu setMenuBarVisible:NO]; 4963e230dd2SCorentin Chary fullScreenWindow = [[NSWindow alloc] initWithContentRect:[[NSScreen mainScreen] frame] 4973e230dd2SCorentin Chary styleMask:NSBorderlessWindowMask 4983e230dd2SCorentin Chary backing:NSBackingStoreBuffered 4993e230dd2SCorentin Chary defer:NO]; 5005d1b2eefSProgrammingkid [fullScreenWindow setAcceptsMouseMovedEvents: YES]; 5013e230dd2SCorentin Chary [fullScreenWindow setHasShadow:NO]; 5025d1b2eefSProgrammingkid [fullScreenWindow setBackgroundColor: [NSColor blackColor]]; 5035d1b2eefSProgrammingkid [self setFrame:NSMakeRect(cx, cy, cw, ch)]; 5045d1b2eefSProgrammingkid [[fullScreenWindow contentView] addSubview: self]; 5053e230dd2SCorentin Chary [fullScreenWindow makeKeyAndOrderFront:self]; 5063e230dd2SCorentin Chary } 5073e230dd2SCorentin Chary } 5083e230dd2SCorentin Chary} 5093e230dd2SCorentin Chary 5103e230dd2SCorentin Chary- (void) handleEvent:(NSEvent *)event 5113e230dd2SCorentin Chary{ 5123e230dd2SCorentin Chary COCOA_DEBUG("QemuCocoaView: handleEvent\n"); 5133e230dd2SCorentin Chary 5143e230dd2SCorentin Chary int buttons = 0; 5153e230dd2SCorentin Chary int keycode; 51621bae11aSGerd Hoffmann bool mouse_event = false; 5173e230dd2SCorentin Chary NSPoint p = [event locationInWindow]; 5183e230dd2SCorentin Chary 5193e230dd2SCorentin Chary switch ([event type]) { 5203e230dd2SCorentin Chary case NSFlagsChanged: 5213e230dd2SCorentin Chary keycode = cocoa_keycode_to_qemu([event keyCode]); 5228895919aSPeter Maydell 523aaac714fSJohn Arbuckle if ((keycode == Q_KEY_CODE_META_L || keycode == Q_KEY_CODE_META_R) 524aaac714fSJohn Arbuckle && !isMouseGrabbed) { 5258895919aSPeter Maydell /* Don't pass command key changes to guest unless mouse is grabbed */ 5268895919aSPeter Maydell keycode = 0; 5278895919aSPeter Maydell } 5288895919aSPeter Maydell 5293e230dd2SCorentin Chary if (keycode) { 530aaac714fSJohn Arbuckle // emulate caps lock and num lock keydown and keyup 531aaac714fSJohn Arbuckle if (keycode == Q_KEY_CODE_CAPS_LOCK || 532aaac714fSJohn Arbuckle keycode == Q_KEY_CODE_NUM_LOCK) { 533aaac714fSJohn Arbuckle qemu_input_event_send_key_qcode(dcl->con, keycode, true); 534aaac714fSJohn Arbuckle qemu_input_event_send_key_qcode(dcl->con, keycode, false); 53568c0aa6eSPeter Maydell } else if (qemu_console_is_graphic(NULL)) { 5363e230dd2SCorentin Chary if (modifiers_state[keycode] == 0) { // keydown 537aaac714fSJohn Arbuckle qemu_input_event_send_key_qcode(dcl->con, keycode, true); 5383e230dd2SCorentin Chary modifiers_state[keycode] = 1; 5393e230dd2SCorentin Chary } else { // keyup 540aaac714fSJohn Arbuckle qemu_input_event_send_key_qcode(dcl->con, keycode, false); 5413e230dd2SCorentin Chary modifiers_state[keycode] = 0; 5423e230dd2SCorentin Chary } 5433e230dd2SCorentin Chary } 5443e230dd2SCorentin Chary } 5453e230dd2SCorentin Chary 5463e230dd2SCorentin Chary // release Mouse grab when pressing ctrl+alt 5475d1b2eefSProgrammingkid if (([event modifierFlags] & NSControlKeyMask) && ([event modifierFlags] & NSAlternateKeyMask)) { 5483e230dd2SCorentin Chary [self ungrabMouse]; 5493e230dd2SCorentin Chary } 5503e230dd2SCorentin Chary break; 5513e230dd2SCorentin Chary case NSKeyDown: 5528895919aSPeter Maydell keycode = cocoa_keycode_to_qemu([event keyCode]); 5533e230dd2SCorentin Chary 5548895919aSPeter Maydell // forward command key combos to the host UI unless the mouse is grabbed 55549b9bd4dSPeter Maydell if (!isMouseGrabbed && ([event modifierFlags] & NSCommandKeyMask)) { 5563e230dd2SCorentin Chary [NSApp sendEvent:event]; 5573e230dd2SCorentin Chary return; 5583e230dd2SCorentin Chary } 5593e230dd2SCorentin Chary 5603e230dd2SCorentin Chary // default 5613e230dd2SCorentin Chary 5623e230dd2SCorentin Chary // handle control + alt Key Combos (ctrl+alt is reserved for QEMU) 5633e230dd2SCorentin Chary if (([event modifierFlags] & NSControlKeyMask) && ([event modifierFlags] & NSAlternateKeyMask)) { 5643e230dd2SCorentin Chary switch (keycode) { 5653e230dd2SCorentin Chary 5663e230dd2SCorentin Chary // enable graphic console 567aaac714fSJohn Arbuckle case Q_KEY_CODE_1 ... Q_KEY_CODE_9: // '1' to '9' keys 568aaac714fSJohn Arbuckle console_select(keycode - 11); 5693e230dd2SCorentin Chary break; 5703e230dd2SCorentin Chary } 5713e230dd2SCorentin Chary 5723e230dd2SCorentin Chary // handle keys for graphic console 57368c0aa6eSPeter Maydell } else if (qemu_console_is_graphic(NULL)) { 574aaac714fSJohn Arbuckle qemu_input_event_send_key_qcode(dcl->con, keycode, true); 5753e230dd2SCorentin Chary 5763e230dd2SCorentin Chary // handlekeys for Monitor 5773e230dd2SCorentin Chary } else { 5783e230dd2SCorentin Chary int keysym = 0; 5793e230dd2SCorentin Chary switch([event keyCode]) { 5803e230dd2SCorentin Chary case 115: 5813e230dd2SCorentin Chary keysym = QEMU_KEY_HOME; 5823e230dd2SCorentin Chary break; 5833e230dd2SCorentin Chary case 117: 5843e230dd2SCorentin Chary keysym = QEMU_KEY_DELETE; 5853e230dd2SCorentin Chary break; 5863e230dd2SCorentin Chary case 119: 5873e230dd2SCorentin Chary keysym = QEMU_KEY_END; 5883e230dd2SCorentin Chary break; 5893e230dd2SCorentin Chary case 123: 5903e230dd2SCorentin Chary keysym = QEMU_KEY_LEFT; 5913e230dd2SCorentin Chary break; 5923e230dd2SCorentin Chary case 124: 5933e230dd2SCorentin Chary keysym = QEMU_KEY_RIGHT; 5943e230dd2SCorentin Chary break; 5953e230dd2SCorentin Chary case 125: 5963e230dd2SCorentin Chary keysym = QEMU_KEY_DOWN; 5973e230dd2SCorentin Chary break; 5983e230dd2SCorentin Chary case 126: 5993e230dd2SCorentin Chary keysym = QEMU_KEY_UP; 6003e230dd2SCorentin Chary break; 6013e230dd2SCorentin Chary default: 6023e230dd2SCorentin Chary { 6033e230dd2SCorentin Chary NSString *ks = [event characters]; 6043e230dd2SCorentin Chary if ([ks length] > 0) 6053e230dd2SCorentin Chary keysym = [ks characterAtIndex:0]; 6063e230dd2SCorentin Chary } 6073e230dd2SCorentin Chary } 6083e230dd2SCorentin Chary if (keysym) 6093e230dd2SCorentin Chary kbd_put_keysym(keysym); 6103e230dd2SCorentin Chary } 6113e230dd2SCorentin Chary break; 6123e230dd2SCorentin Chary case NSKeyUp: 6133e230dd2SCorentin Chary keycode = cocoa_keycode_to_qemu([event keyCode]); 6148895919aSPeter Maydell 6158895919aSPeter Maydell // don't pass the guest a spurious key-up if we treated this 6168895919aSPeter Maydell // command-key combo as a host UI action 61749b9bd4dSPeter Maydell if (!isMouseGrabbed && ([event modifierFlags] & NSCommandKeyMask)) { 6188895919aSPeter Maydell return; 6198895919aSPeter Maydell } 6208895919aSPeter Maydell 62168c0aa6eSPeter Maydell if (qemu_console_is_graphic(NULL)) { 622aaac714fSJohn Arbuckle qemu_input_event_send_key_qcode(dcl->con, keycode, false); 6233e230dd2SCorentin Chary } 6243e230dd2SCorentin Chary break; 6253e230dd2SCorentin Chary case NSMouseMoved: 6263e230dd2SCorentin Chary if (isAbsoluteEnabled) { 6275dd45beeSPeter Maydell if (![self screenContainsPoint:p] || ![[self window] isKeyWindow]) { 628f61c387eSPeter Maydell if (isMouseGrabbed) { 629f61c387eSPeter Maydell [self ungrabMouse]; 6303e230dd2SCorentin Chary } 6313e230dd2SCorentin Chary } else { 632f61c387eSPeter Maydell if (!isMouseGrabbed) { 633f61c387eSPeter Maydell [self grabMouse]; 6343e230dd2SCorentin Chary } 6353e230dd2SCorentin Chary } 6363e230dd2SCorentin Chary } 63721bae11aSGerd Hoffmann mouse_event = true; 6383e230dd2SCorentin Chary break; 6393e230dd2SCorentin Chary case NSLeftMouseDown: 6403e230dd2SCorentin Chary if ([event modifierFlags] & NSCommandKeyMask) { 6413e230dd2SCorentin Chary buttons |= MOUSE_EVENT_RBUTTON; 6423e230dd2SCorentin Chary } else { 6433e230dd2SCorentin Chary buttons |= MOUSE_EVENT_LBUTTON; 6443e230dd2SCorentin Chary } 64521bae11aSGerd Hoffmann mouse_event = true; 6463e230dd2SCorentin Chary break; 6473e230dd2SCorentin Chary case NSRightMouseDown: 6483e230dd2SCorentin Chary buttons |= MOUSE_EVENT_RBUTTON; 64921bae11aSGerd Hoffmann mouse_event = true; 6503e230dd2SCorentin Chary break; 6513e230dd2SCorentin Chary case NSOtherMouseDown: 6523e230dd2SCorentin Chary buttons |= MOUSE_EVENT_MBUTTON; 65321bae11aSGerd Hoffmann mouse_event = true; 6543e230dd2SCorentin Chary break; 6553e230dd2SCorentin Chary case NSLeftMouseDragged: 6563e230dd2SCorentin Chary if ([event modifierFlags] & NSCommandKeyMask) { 6573e230dd2SCorentin Chary buttons |= MOUSE_EVENT_RBUTTON; 6583e230dd2SCorentin Chary } else { 6593e230dd2SCorentin Chary buttons |= MOUSE_EVENT_LBUTTON; 6603e230dd2SCorentin Chary } 66121bae11aSGerd Hoffmann mouse_event = true; 6623e230dd2SCorentin Chary break; 6633e230dd2SCorentin Chary case NSRightMouseDragged: 6643e230dd2SCorentin Chary buttons |= MOUSE_EVENT_RBUTTON; 66521bae11aSGerd Hoffmann mouse_event = true; 6663e230dd2SCorentin Chary break; 6673e230dd2SCorentin Chary case NSOtherMouseDragged: 6683e230dd2SCorentin Chary buttons |= MOUSE_EVENT_MBUTTON; 66921bae11aSGerd Hoffmann mouse_event = true; 6703e230dd2SCorentin Chary break; 6713e230dd2SCorentin Chary case NSLeftMouseUp: 67221bae11aSGerd Hoffmann mouse_event = true; 673f61c387eSPeter Maydell if (!isMouseGrabbed && [self screenContainsPoint:p]) { 6749e8204b1SProgrammingkid if([[self window] isKeyWindow]) { 6753e230dd2SCorentin Chary [self grabMouse]; 6763e230dd2SCorentin Chary } 6779e8204b1SProgrammingkid } 6783e230dd2SCorentin Chary break; 6793e230dd2SCorentin Chary case NSRightMouseUp: 68021bae11aSGerd Hoffmann mouse_event = true; 6813e230dd2SCorentin Chary break; 6823e230dd2SCorentin Chary case NSOtherMouseUp: 68321bae11aSGerd Hoffmann mouse_event = true; 6843e230dd2SCorentin Chary break; 6853e230dd2SCorentin Chary case NSScrollWheel: 686f61c387eSPeter Maydell if (isMouseGrabbed) { 68721bae11aSGerd Hoffmann buttons |= ([event deltaY] < 0) ? 68821bae11aSGerd Hoffmann MOUSE_EVENT_WHEELUP : MOUSE_EVENT_WHEELDN; 6893e230dd2SCorentin Chary } 690f61c387eSPeter Maydell mouse_event = true; 6913e230dd2SCorentin Chary break; 6923e230dd2SCorentin Chary default: 6933e230dd2SCorentin Chary [NSApp sendEvent:event]; 6943e230dd2SCorentin Chary } 69521bae11aSGerd Hoffmann 69621bae11aSGerd Hoffmann if (mouse_event) { 6978d3a5d9bSPeter Maydell /* Don't send button events to the guest unless we've got a 6988d3a5d9bSPeter Maydell * mouse grab or window focus. If we have neither then this event 6998d3a5d9bSPeter Maydell * is the user clicking on the background window to activate and 7008d3a5d9bSPeter Maydell * bring us to the front, which will be done by the sendEvent 7018d3a5d9bSPeter Maydell * call below. We definitely don't want to pass that click through 7028d3a5d9bSPeter Maydell * to the guest. 7038d3a5d9bSPeter Maydell */ 7048d3a5d9bSPeter Maydell if ((isMouseGrabbed || [[self window] isKeyWindow]) && 7058d3a5d9bSPeter Maydell (last_buttons != buttons)) { 7067fb1cf16SEric Blake static uint32_t bmap[INPUT_BUTTON__MAX] = { 70721bae11aSGerd Hoffmann [INPUT_BUTTON_LEFT] = MOUSE_EVENT_LBUTTON, 70821bae11aSGerd Hoffmann [INPUT_BUTTON_MIDDLE] = MOUSE_EVENT_MBUTTON, 70921bae11aSGerd Hoffmann [INPUT_BUTTON_RIGHT] = MOUSE_EVENT_RBUTTON, 710f22d0af0SGerd Hoffmann [INPUT_BUTTON_WHEEL_UP] = MOUSE_EVENT_WHEELUP, 711f22d0af0SGerd Hoffmann [INPUT_BUTTON_WHEEL_DOWN] = MOUSE_EVENT_WHEELDN, 71221bae11aSGerd Hoffmann }; 71321bae11aSGerd Hoffmann qemu_input_update_buttons(dcl->con, bmap, last_buttons, buttons); 71421bae11aSGerd Hoffmann last_buttons = buttons; 71521bae11aSGerd Hoffmann } 716f61c387eSPeter Maydell if (isMouseGrabbed) { 717f61c387eSPeter Maydell if (isAbsoluteEnabled) { 718f61c387eSPeter Maydell /* Note that the origin for Cocoa mouse coords is bottom left, not top left. 719f61c387eSPeter Maydell * The check on screenContainsPoint is to avoid sending out of range values for 720f61c387eSPeter Maydell * clicks in the titlebar. 721f61c387eSPeter Maydell */ 722f61c387eSPeter Maydell if ([self screenContainsPoint:p]) { 72321bae11aSGerd Hoffmann qemu_input_queue_abs(dcl->con, INPUT_AXIS_X, p.x, screen.width); 724f61c387eSPeter Maydell qemu_input_queue_abs(dcl->con, INPUT_AXIS_Y, screen.height - p.y, screen.height); 725f61c387eSPeter Maydell } 726f61c387eSPeter Maydell } else { 72721bae11aSGerd Hoffmann qemu_input_queue_rel(dcl->con, INPUT_AXIS_X, (int)[event deltaX]); 72821bae11aSGerd Hoffmann qemu_input_queue_rel(dcl->con, INPUT_AXIS_Y, (int)[event deltaY]); 729f61c387eSPeter Maydell } 73021bae11aSGerd Hoffmann } else { 73121bae11aSGerd Hoffmann [NSApp sendEvent:event]; 73221bae11aSGerd Hoffmann } 73321bae11aSGerd Hoffmann qemu_input_event_sync(); 73421bae11aSGerd Hoffmann } 7353e230dd2SCorentin Chary} 7363e230dd2SCorentin Chary 7373e230dd2SCorentin Chary- (void) grabMouse 7383e230dd2SCorentin Chary{ 7393e230dd2SCorentin Chary COCOA_DEBUG("QemuCocoaView: grabMouse\n"); 7403e230dd2SCorentin Chary 7413e230dd2SCorentin Chary if (!isFullscreen) { 7423e230dd2SCorentin Chary if (qemu_name) 7433e230dd2SCorentin Chary [normalWindow setTitle:[NSString stringWithFormat:@"QEMU %s - (Press ctrl + alt to release Mouse)", qemu_name]]; 7443e230dd2SCorentin Chary else 7453e230dd2SCorentin Chary [normalWindow setTitle:@"QEMU - (Press ctrl + alt to release Mouse)"]; 7463e230dd2SCorentin Chary } 74713aefd30SPeter Maydell [self hideCursor]; 748f61c387eSPeter Maydell if (!isAbsoluteEnabled) { 749f61c387eSPeter Maydell isMouseDeassociated = TRUE; 7503e230dd2SCorentin Chary CGAssociateMouseAndMouseCursorPosition(FALSE); 751f61c387eSPeter Maydell } 75249b9bd4dSPeter Maydell isMouseGrabbed = TRUE; // while isMouseGrabbed = TRUE, QemuCocoaApp sends all events to [cocoaView handleEvent:] 7533e230dd2SCorentin Chary} 7543e230dd2SCorentin Chary 7553e230dd2SCorentin Chary- (void) ungrabMouse 7563e230dd2SCorentin Chary{ 7573e230dd2SCorentin Chary COCOA_DEBUG("QemuCocoaView: ungrabMouse\n"); 7583e230dd2SCorentin Chary 7593e230dd2SCorentin Chary if (!isFullscreen) { 7603e230dd2SCorentin Chary if (qemu_name) 7613e230dd2SCorentin Chary [normalWindow setTitle:[NSString stringWithFormat:@"QEMU %s", qemu_name]]; 7623e230dd2SCorentin Chary else 7633e230dd2SCorentin Chary [normalWindow setTitle:@"QEMU"]; 7643e230dd2SCorentin Chary } 76513aefd30SPeter Maydell [self unhideCursor]; 766f61c387eSPeter Maydell if (isMouseDeassociated) { 7673e230dd2SCorentin Chary CGAssociateMouseAndMouseCursorPosition(TRUE); 768f61c387eSPeter Maydell isMouseDeassociated = FALSE; 769f61c387eSPeter Maydell } 77049b9bd4dSPeter Maydell isMouseGrabbed = FALSE; 7713e230dd2SCorentin Chary} 7723e230dd2SCorentin Chary 7733e230dd2SCorentin Chary- (void) setAbsoluteEnabled:(BOOL)tIsAbsoluteEnabled {isAbsoluteEnabled = tIsAbsoluteEnabled;} 77449b9bd4dSPeter Maydell- (BOOL) isMouseGrabbed {return isMouseGrabbed;} 7753e230dd2SCorentin Chary- (BOOL) isAbsoluteEnabled {return isAbsoluteEnabled;} 776f61c387eSPeter Maydell- (BOOL) isMouseDeassociated {return isMouseDeassociated;} 7773e230dd2SCorentin Chary- (float) cdx {return cdx;} 7783e230dd2SCorentin Chary- (float) cdy {return cdy;} 7793e230dd2SCorentin Chary- (QEMUScreen) gscreen {return screen;} 7803b178b71SJohn Arbuckle 7813b178b71SJohn Arbuckle/* 7823b178b71SJohn Arbuckle * Makes the target think all down keys are being released. 7833b178b71SJohn Arbuckle * This prevents a stuck key problem, since we will not see 7843b178b71SJohn Arbuckle * key up events for those keys after we have lost focus. 7853b178b71SJohn Arbuckle */ 7863b178b71SJohn Arbuckle- (void) raiseAllKeys 7873b178b71SJohn Arbuckle{ 7883b178b71SJohn Arbuckle int index; 7893b178b71SJohn Arbuckle const int max_index = ARRAY_SIZE(modifiers_state); 7903b178b71SJohn Arbuckle 7913b178b71SJohn Arbuckle for (index = 0; index < max_index; index++) { 7923b178b71SJohn Arbuckle if (modifiers_state[index]) { 7933b178b71SJohn Arbuckle modifiers_state[index] = 0; 794aaac714fSJohn Arbuckle qemu_input_event_send_key_qcode(dcl->con, index, false); 7953b178b71SJohn Arbuckle } 7963b178b71SJohn Arbuckle } 7973b178b71SJohn Arbuckle} 7983e230dd2SCorentin Chary@end 7993e230dd2SCorentin Chary 8003e230dd2SCorentin Chary 8013e230dd2SCorentin Chary 8023e230dd2SCorentin Chary/* 8033e230dd2SCorentin Chary ------------------------------------------------------ 8043e230dd2SCorentin Chary QemuCocoaAppController 8053e230dd2SCorentin Chary ------------------------------------------------------ 8063e230dd2SCorentin Chary*/ 8073e230dd2SCorentin Chary@interface QemuCocoaAppController : NSObject 8082a4c8c53SPeter Maydell#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6) 809d9bc14f6SJohn Arbuckle <NSWindowDelegate, NSApplicationDelegate> 8102a4c8c53SPeter Maydell#endif 8113e230dd2SCorentin Chary{ 8123e230dd2SCorentin Chary} 8133e230dd2SCorentin Chary- (void)startEmulationWithArgc:(int)argc argv:(char**)argv; 8145d1b2eefSProgrammingkid- (void)doToggleFullScreen:(id)sender; 8153e230dd2SCorentin Chary- (void)toggleFullScreen:(id)sender; 8163e230dd2SCorentin Chary- (void)showQEMUDoc:(id)sender; 8175d1b2eefSProgrammingkid- (void)zoomToFit:(id) sender; 818b4c6a112SProgrammingkid- (void)displayConsole:(id)sender; 8198524f1c7SJohn Arbuckle- (void)pauseQEMU:(id)sender; 8208524f1c7SJohn Arbuckle- (void)resumeQEMU:(id)sender; 8218524f1c7SJohn Arbuckle- (void)displayPause; 8228524f1c7SJohn Arbuckle- (void)removePause; 82327074614SJohn Arbuckle- (void)restartQEMU:(id)sender; 82427074614SJohn Arbuckle- (void)powerDownQEMU:(id)sender; 825693a3e01SJohn Arbuckle- (void)ejectDeviceMedia:(id)sender; 826693a3e01SJohn Arbuckle- (void)changeDeviceMedia:(id)sender; 827d9bc14f6SJohn Arbuckle- (BOOL)verifyQuit; 828f4747900SJohn Arbuckle- (void)openDocumentation:(NSString *)filename; 8299e8204b1SProgrammingkid- (IBAction) do_about_menu_item: (id) sender; 8309e8204b1SProgrammingkid- (void)make_about_window; 8313e230dd2SCorentin Chary@end 8323e230dd2SCorentin Chary 8333e230dd2SCorentin Chary@implementation QemuCocoaAppController 8343e230dd2SCorentin Chary- (id) init 8353e230dd2SCorentin Chary{ 8363e230dd2SCorentin Chary COCOA_DEBUG("QemuCocoaAppController: init\n"); 8373e230dd2SCorentin Chary 8383e230dd2SCorentin Chary self = [super init]; 8393e230dd2SCorentin Chary if (self) { 8403e230dd2SCorentin Chary 8413e230dd2SCorentin Chary // create a view and add it to the window 8423e230dd2SCorentin Chary cocoaView = [[QemuCocoaView alloc] initWithFrame:NSMakeRect(0.0, 0.0, 640.0, 480.0)]; 8433e230dd2SCorentin Chary if(!cocoaView) { 8443e230dd2SCorentin Chary fprintf(stderr, "(cocoa) can't create a view\n"); 8453e230dd2SCorentin Chary exit(1); 8463e230dd2SCorentin Chary } 8473e230dd2SCorentin Chary 8483e230dd2SCorentin Chary // create a window 8493e230dd2SCorentin Chary normalWindow = [[NSWindow alloc] initWithContentRect:[cocoaView frame] 8503e230dd2SCorentin Chary styleMask:NSTitledWindowMask|NSMiniaturizableWindowMask|NSClosableWindowMask 8513e230dd2SCorentin Chary backing:NSBackingStoreBuffered defer:NO]; 8523e230dd2SCorentin Chary if(!normalWindow) { 8533e230dd2SCorentin Chary fprintf(stderr, "(cocoa) can't create window\n"); 8543e230dd2SCorentin Chary exit(1); 8553e230dd2SCorentin Chary } 8563e230dd2SCorentin Chary [normalWindow setAcceptsMouseMovedEvents:YES]; 857a1dbc05aSJohn Arbuckle [normalWindow setTitle:@"QEMU"]; 8583e230dd2SCorentin Chary [normalWindow setContentView:cocoaView]; 85981801ae2SPeter Maydell#if (MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_10) 8603e230dd2SCorentin Chary [normalWindow useOptimizedDrawing:YES]; 86181801ae2SPeter Maydell#endif 8623e230dd2SCorentin Chary [normalWindow makeKeyAndOrderFront:self]; 8633e230dd2SCorentin Chary [normalWindow center]; 864d9bc14f6SJohn Arbuckle [normalWindow setDelegate: self]; 8655d1b2eefSProgrammingkid stretch_video = false; 8668524f1c7SJohn Arbuckle 8678524f1c7SJohn Arbuckle /* Used for displaying pause on the screen */ 8688524f1c7SJohn Arbuckle pauseLabel = [NSTextField new]; 8698524f1c7SJohn Arbuckle [pauseLabel setBezeled:YES]; 8708524f1c7SJohn Arbuckle [pauseLabel setDrawsBackground:YES]; 8718524f1c7SJohn Arbuckle [pauseLabel setBackgroundColor: [NSColor whiteColor]]; 8728524f1c7SJohn Arbuckle [pauseLabel setEditable:NO]; 8738524f1c7SJohn Arbuckle [pauseLabel setSelectable:NO]; 8748524f1c7SJohn Arbuckle [pauseLabel setStringValue: @"Paused"]; 8758524f1c7SJohn Arbuckle [pauseLabel setFont: [NSFont fontWithName: @"Helvetica" size: 90]]; 8768524f1c7SJohn Arbuckle [pauseLabel setTextColor: [NSColor blackColor]]; 8778524f1c7SJohn Arbuckle [pauseLabel sizeToFit]; 878693a3e01SJohn Arbuckle 879693a3e01SJohn Arbuckle // set the supported image file types that can be opened 880693a3e01SJohn Arbuckle supportedImageFileTypes = [NSArray arrayWithObjects: @"img", @"iso", @"dmg", 8819d227f19SJohn Arbuckle @"qcow", @"qcow2", @"cloop", @"vmdk", @"cdr", 882*5f26fcfbSProgrammingkid @"toast", nil]; 8839e8204b1SProgrammingkid [self make_about_window]; 8843e230dd2SCorentin Chary } 8853e230dd2SCorentin Chary return self; 8863e230dd2SCorentin Chary} 8873e230dd2SCorentin Chary 8883e230dd2SCorentin Chary- (void) dealloc 8893e230dd2SCorentin Chary{ 8903e230dd2SCorentin Chary COCOA_DEBUG("QemuCocoaAppController: dealloc\n"); 8913e230dd2SCorentin Chary 8923e230dd2SCorentin Chary if (cocoaView) 8933e230dd2SCorentin Chary [cocoaView release]; 8943e230dd2SCorentin Chary [super dealloc]; 8953e230dd2SCorentin Chary} 8963e230dd2SCorentin Chary 8973e230dd2SCorentin Chary- (void)applicationDidFinishLaunching: (NSNotification *) note 8983e230dd2SCorentin Chary{ 8993e230dd2SCorentin Chary COCOA_DEBUG("QemuCocoaAppController: applicationDidFinishLaunching\n"); 900365d7f3cSJohn Arbuckle // launch QEMU, with the global args 9013e230dd2SCorentin Chary [self startEmulationWithArgc:gArgc argv:(char **)gArgv]; 9023e230dd2SCorentin Chary} 9033e230dd2SCorentin Chary 9043e230dd2SCorentin Chary- (void)applicationWillTerminate:(NSNotification *)aNotification 9053e230dd2SCorentin Chary{ 9063e230dd2SCorentin Chary COCOA_DEBUG("QemuCocoaAppController: applicationWillTerminate\n"); 9073e230dd2SCorentin Chary 9083e230dd2SCorentin Chary qemu_system_shutdown_request(); 9093e230dd2SCorentin Chary exit(0); 9103e230dd2SCorentin Chary} 9113e230dd2SCorentin Chary 9123e230dd2SCorentin Chary- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)theApplication 9133e230dd2SCorentin Chary{ 9143e230dd2SCorentin Chary return YES; 9153e230dd2SCorentin Chary} 9163e230dd2SCorentin Chary 917d9bc14f6SJohn Arbuckle- (NSApplicationTerminateReply)applicationShouldTerminate: 918d9bc14f6SJohn Arbuckle (NSApplication *)sender 919d9bc14f6SJohn Arbuckle{ 920d9bc14f6SJohn Arbuckle COCOA_DEBUG("QemuCocoaAppController: applicationShouldTerminate\n"); 921d9bc14f6SJohn Arbuckle return [self verifyQuit]; 922d9bc14f6SJohn Arbuckle} 923d9bc14f6SJohn Arbuckle 924d9bc14f6SJohn Arbuckle/* Called when the user clicks on a window's close button */ 925d9bc14f6SJohn Arbuckle- (BOOL)windowShouldClose:(id)sender 926d9bc14f6SJohn Arbuckle{ 927d9bc14f6SJohn Arbuckle COCOA_DEBUG("QemuCocoaAppController: windowShouldClose\n"); 928d9bc14f6SJohn Arbuckle [NSApp terminate: sender]; 929d9bc14f6SJohn Arbuckle /* If the user allows the application to quit then the call to 930d9bc14f6SJohn Arbuckle * NSApp terminate will never return. If we get here then the user 931d9bc14f6SJohn Arbuckle * cancelled the quit, so we should return NO to not permit the 932d9bc14f6SJohn Arbuckle * closing of this window. 933d9bc14f6SJohn Arbuckle */ 934d9bc14f6SJohn Arbuckle return NO; 935d9bc14f6SJohn Arbuckle} 936d9bc14f6SJohn Arbuckle 9373b178b71SJohn Arbuckle/* Called when QEMU goes into the background */ 9383b178b71SJohn Arbuckle- (void) applicationWillResignActive: (NSNotification *)aNotification 9393b178b71SJohn Arbuckle{ 9403b178b71SJohn Arbuckle COCOA_DEBUG("QemuCocoaAppController: applicationWillResignActive\n"); 9413b178b71SJohn Arbuckle [cocoaView raiseAllKeys]; 9423b178b71SJohn Arbuckle} 9433b178b71SJohn Arbuckle 9443e230dd2SCorentin Chary- (void)startEmulationWithArgc:(int)argc argv:(char**)argv 9453e230dd2SCorentin Chary{ 9463e230dd2SCorentin Chary COCOA_DEBUG("QemuCocoaAppController: startEmulationWithArgc\n"); 9473e230dd2SCorentin Chary 9483e230dd2SCorentin Chary int status; 9493bbbee18SAndreas Färber status = qemu_main(argc, argv, *_NSGetEnviron()); 9503e230dd2SCorentin Chary exit(status); 9513e230dd2SCorentin Chary} 9523e230dd2SCorentin Chary 9535d1b2eefSProgrammingkid/* We abstract the method called by the Enter Fullscreen menu item 9545d1b2eefSProgrammingkid * because Mac OS 10.7 and higher disables it. This is because of the 9555d1b2eefSProgrammingkid * menu item's old selector's name toggleFullScreen: 9565d1b2eefSProgrammingkid */ 9575d1b2eefSProgrammingkid- (void) doToggleFullScreen:(id)sender 9585d1b2eefSProgrammingkid{ 9595d1b2eefSProgrammingkid [self toggleFullScreen:(id)sender]; 9605d1b2eefSProgrammingkid} 9615d1b2eefSProgrammingkid 9623e230dd2SCorentin Chary- (void)toggleFullScreen:(id)sender 9633e230dd2SCorentin Chary{ 9643e230dd2SCorentin Chary COCOA_DEBUG("QemuCocoaAppController: toggleFullScreen\n"); 9653e230dd2SCorentin Chary 9663e230dd2SCorentin Chary [cocoaView toggleFullScreen:sender]; 9673e230dd2SCorentin Chary} 9683e230dd2SCorentin Chary 969f4747900SJohn Arbuckle/* Tries to find then open the specified filename */ 970f4747900SJohn Arbuckle- (void) openDocumentation: (NSString *) filename 971f4747900SJohn Arbuckle{ 972f4747900SJohn Arbuckle /* Where to look for local files */ 973f4747900SJohn Arbuckle NSString *path_array[] = {@"../share/doc/qemu/", @"../doc/qemu/", @"../"}; 974f4747900SJohn Arbuckle NSString *full_file_path; 975f4747900SJohn Arbuckle 976f4747900SJohn Arbuckle /* iterate thru the possible paths until the file is found */ 977f4747900SJohn Arbuckle int index; 978f4747900SJohn Arbuckle for (index = 0; index < ARRAY_SIZE(path_array); index++) { 979f4747900SJohn Arbuckle full_file_path = [[NSBundle mainBundle] executablePath]; 980f4747900SJohn Arbuckle full_file_path = [full_file_path stringByDeletingLastPathComponent]; 981f4747900SJohn Arbuckle full_file_path = [NSString stringWithFormat: @"%@/%@%@", full_file_path, 982f4747900SJohn Arbuckle path_array[index], filename]; 983f4747900SJohn Arbuckle if ([[NSWorkspace sharedWorkspace] openFile: full_file_path] == YES) { 984f4747900SJohn Arbuckle return; 985f4747900SJohn Arbuckle } 986f4747900SJohn Arbuckle } 987f4747900SJohn Arbuckle 988f4747900SJohn Arbuckle /* If none of the paths opened a file */ 989f4747900SJohn Arbuckle NSBeep(); 990f4747900SJohn Arbuckle QEMU_Alert(@"Failed to open file"); 991f4747900SJohn Arbuckle} 992f4747900SJohn Arbuckle 9933e230dd2SCorentin Chary- (void)showQEMUDoc:(id)sender 9943e230dd2SCorentin Chary{ 9953e230dd2SCorentin Chary COCOA_DEBUG("QemuCocoaAppController: showQEMUDoc\n"); 9963e230dd2SCorentin Chary 997f4747900SJohn Arbuckle [self openDocumentation: @"qemu-doc.html"]; 9983e230dd2SCorentin Chary} 9993e230dd2SCorentin Chary 10005d1b2eefSProgrammingkid/* Stretches video to fit host monitor size */ 10015d1b2eefSProgrammingkid- (void)zoomToFit:(id) sender 10025d1b2eefSProgrammingkid{ 10035d1b2eefSProgrammingkid stretch_video = !stretch_video; 10045d1b2eefSProgrammingkid if (stretch_video == true) { 10055d1b2eefSProgrammingkid [sender setState: NSOnState]; 10065d1b2eefSProgrammingkid } else { 10075d1b2eefSProgrammingkid [sender setState: NSOffState]; 10085d1b2eefSProgrammingkid } 10095d1b2eefSProgrammingkid} 10103e230dd2SCorentin Chary 1011b4c6a112SProgrammingkid/* Displays the console on the screen */ 1012b4c6a112SProgrammingkid- (void)displayConsole:(id)sender 1013b4c6a112SProgrammingkid{ 1014b4c6a112SProgrammingkid console_select([sender tag]); 1015b4c6a112SProgrammingkid} 10168524f1c7SJohn Arbuckle 10178524f1c7SJohn Arbuckle/* Pause the guest */ 10188524f1c7SJohn Arbuckle- (void)pauseQEMU:(id)sender 10198524f1c7SJohn Arbuckle{ 10208524f1c7SJohn Arbuckle qmp_stop(NULL); 10218524f1c7SJohn Arbuckle [sender setEnabled: NO]; 10228524f1c7SJohn Arbuckle [[[sender menu] itemWithTitle: @"Resume"] setEnabled: YES]; 10238524f1c7SJohn Arbuckle [self displayPause]; 10248524f1c7SJohn Arbuckle} 10258524f1c7SJohn Arbuckle 10268524f1c7SJohn Arbuckle/* Resume running the guest operating system */ 10278524f1c7SJohn Arbuckle- (void)resumeQEMU:(id) sender 10288524f1c7SJohn Arbuckle{ 10298524f1c7SJohn Arbuckle qmp_cont(NULL); 10308524f1c7SJohn Arbuckle [sender setEnabled: NO]; 10318524f1c7SJohn Arbuckle [[[sender menu] itemWithTitle: @"Pause"] setEnabled: YES]; 10328524f1c7SJohn Arbuckle [self removePause]; 10338524f1c7SJohn Arbuckle} 10348524f1c7SJohn Arbuckle 10358524f1c7SJohn Arbuckle/* Displays the word pause on the screen */ 10368524f1c7SJohn Arbuckle- (void)displayPause 10378524f1c7SJohn Arbuckle{ 10388524f1c7SJohn Arbuckle /* Coordinates have to be calculated each time because the window can change its size */ 10398524f1c7SJohn Arbuckle int xCoord, yCoord, width, height; 10408524f1c7SJohn Arbuckle xCoord = ([normalWindow frame].size.width - [pauseLabel frame].size.width)/2; 10418524f1c7SJohn Arbuckle yCoord = [normalWindow frame].size.height - [pauseLabel frame].size.height - ([pauseLabel frame].size.height * .5); 10428524f1c7SJohn Arbuckle width = [pauseLabel frame].size.width; 10438524f1c7SJohn Arbuckle height = [pauseLabel frame].size.height; 10448524f1c7SJohn Arbuckle [pauseLabel setFrame: NSMakeRect(xCoord, yCoord, width, height)]; 10458524f1c7SJohn Arbuckle [cocoaView addSubview: pauseLabel]; 10468524f1c7SJohn Arbuckle} 10478524f1c7SJohn Arbuckle 10488524f1c7SJohn Arbuckle/* Removes the word pause from the screen */ 10498524f1c7SJohn Arbuckle- (void)removePause 10508524f1c7SJohn Arbuckle{ 10518524f1c7SJohn Arbuckle [pauseLabel removeFromSuperview]; 10528524f1c7SJohn Arbuckle} 10538524f1c7SJohn Arbuckle 105427074614SJohn Arbuckle/* Restarts QEMU */ 105527074614SJohn Arbuckle- (void)restartQEMU:(id)sender 105627074614SJohn Arbuckle{ 105727074614SJohn Arbuckle qmp_system_reset(NULL); 105827074614SJohn Arbuckle} 105927074614SJohn Arbuckle 106027074614SJohn Arbuckle/* Powers down QEMU */ 106127074614SJohn Arbuckle- (void)powerDownQEMU:(id)sender 106227074614SJohn Arbuckle{ 106327074614SJohn Arbuckle qmp_system_powerdown(NULL); 106427074614SJohn Arbuckle} 106527074614SJohn Arbuckle 1066693a3e01SJohn Arbuckle/* Ejects the media. 1067693a3e01SJohn Arbuckle * Uses sender's tag to figure out the device to eject. 1068693a3e01SJohn Arbuckle */ 1069693a3e01SJohn Arbuckle- (void)ejectDeviceMedia:(id)sender 1070693a3e01SJohn Arbuckle{ 1071693a3e01SJohn Arbuckle NSString * drive; 1072693a3e01SJohn Arbuckle drive = [sender representedObject]; 1073693a3e01SJohn Arbuckle if(drive == nil) { 1074693a3e01SJohn Arbuckle NSBeep(); 1075693a3e01SJohn Arbuckle QEMU_Alert(@"Failed to find drive to eject!"); 1076693a3e01SJohn Arbuckle return; 1077693a3e01SJohn Arbuckle } 1078693a3e01SJohn Arbuckle 1079693a3e01SJohn Arbuckle Error *err = NULL; 1080fbe2d816SKevin Wolf qmp_eject(true, [drive cStringUsingEncoding: NSASCIIStringEncoding], 1081fbe2d816SKevin Wolf false, NULL, false, false, &err); 1082693a3e01SJohn Arbuckle handleAnyDeviceErrors(err); 1083693a3e01SJohn Arbuckle} 1084693a3e01SJohn Arbuckle 1085693a3e01SJohn Arbuckle/* Displays a dialog box asking the user to select an image file to load. 1086693a3e01SJohn Arbuckle * Uses sender's represented object value to figure out which drive to use. 1087693a3e01SJohn Arbuckle */ 1088693a3e01SJohn Arbuckle- (void)changeDeviceMedia:(id)sender 1089693a3e01SJohn Arbuckle{ 1090693a3e01SJohn Arbuckle /* Find the drive name */ 1091693a3e01SJohn Arbuckle NSString * drive; 1092693a3e01SJohn Arbuckle drive = [sender representedObject]; 1093693a3e01SJohn Arbuckle if(drive == nil) { 1094693a3e01SJohn Arbuckle NSBeep(); 1095693a3e01SJohn Arbuckle QEMU_Alert(@"Could not find drive!"); 1096693a3e01SJohn Arbuckle return; 1097693a3e01SJohn Arbuckle } 1098693a3e01SJohn Arbuckle 1099693a3e01SJohn Arbuckle /* Display the file open dialog */ 1100693a3e01SJohn Arbuckle NSOpenPanel * openPanel; 1101693a3e01SJohn Arbuckle openPanel = [NSOpenPanel openPanel]; 1102693a3e01SJohn Arbuckle [openPanel setCanChooseFiles: YES]; 1103693a3e01SJohn Arbuckle [openPanel setAllowsMultipleSelection: NO]; 1104693a3e01SJohn Arbuckle [openPanel setAllowedFileTypes: supportedImageFileTypes]; 1105693a3e01SJohn Arbuckle if([openPanel runModal] == NSFileHandlingPanelOKButton) { 1106693a3e01SJohn Arbuckle NSString * file = [[[openPanel URLs] objectAtIndex: 0] path]; 1107693a3e01SJohn Arbuckle if(file == nil) { 1108693a3e01SJohn Arbuckle NSBeep(); 1109693a3e01SJohn Arbuckle QEMU_Alert(@"Failed to convert URL to file path!"); 1110693a3e01SJohn Arbuckle return; 1111693a3e01SJohn Arbuckle } 1112693a3e01SJohn Arbuckle 1113693a3e01SJohn Arbuckle Error *err = NULL; 111470e2cb3bSKevin Wolf qmp_blockdev_change_medium(true, 111570e2cb3bSKevin Wolf [drive cStringUsingEncoding: 111624fb4133SMax Reitz NSASCIIStringEncoding], 111770e2cb3bSKevin Wolf false, NULL, 111824fb4133SMax Reitz [file cStringUsingEncoding: 111924fb4133SMax Reitz NSASCIIStringEncoding], 112024fb4133SMax Reitz true, "raw", 112139ff43d9SMax Reitz false, 0, 1122693a3e01SJohn Arbuckle &err); 1123693a3e01SJohn Arbuckle handleAnyDeviceErrors(err); 1124693a3e01SJohn Arbuckle } 1125693a3e01SJohn Arbuckle} 1126693a3e01SJohn Arbuckle 1127d9bc14f6SJohn Arbuckle/* Verifies if the user really wants to quit */ 1128d9bc14f6SJohn Arbuckle- (BOOL)verifyQuit 1129d9bc14f6SJohn Arbuckle{ 1130d9bc14f6SJohn Arbuckle NSAlert *alert = [NSAlert new]; 1131d9bc14f6SJohn Arbuckle [alert autorelease]; 1132d9bc14f6SJohn Arbuckle [alert setMessageText: @"Are you sure you want to quit QEMU?"]; 1133d9bc14f6SJohn Arbuckle [alert addButtonWithTitle: @"Cancel"]; 1134d9bc14f6SJohn Arbuckle [alert addButtonWithTitle: @"Quit"]; 1135d9bc14f6SJohn Arbuckle if([alert runModal] == NSAlertSecondButtonReturn) { 1136d9bc14f6SJohn Arbuckle return YES; 1137d9bc14f6SJohn Arbuckle } else { 1138d9bc14f6SJohn Arbuckle return NO; 1139d9bc14f6SJohn Arbuckle } 1140d9bc14f6SJohn Arbuckle} 1141d9bc14f6SJohn Arbuckle 11429e8204b1SProgrammingkid/* The action method for the About menu item */ 11439e8204b1SProgrammingkid- (IBAction) do_about_menu_item: (id) sender 11449e8204b1SProgrammingkid{ 11459e8204b1SProgrammingkid [about_window makeKeyAndOrderFront: nil]; 11469e8204b1SProgrammingkid} 11479e8204b1SProgrammingkid 11489e8204b1SProgrammingkid/* Create and display the about dialog */ 11499e8204b1SProgrammingkid- (void)make_about_window 11509e8204b1SProgrammingkid{ 11519e8204b1SProgrammingkid /* Make the window */ 11529e8204b1SProgrammingkid int x = 0, y = 0, about_width = 400, about_height = 200; 11539e8204b1SProgrammingkid NSRect window_rect = NSMakeRect(x, y, about_width, about_height); 11549e8204b1SProgrammingkid about_window = [[NSWindow alloc] initWithContentRect:window_rect 11559e8204b1SProgrammingkid styleMask:NSTitledWindowMask | NSClosableWindowMask | 11569e8204b1SProgrammingkid NSMiniaturizableWindowMask 11579e8204b1SProgrammingkid backing:NSBackingStoreBuffered 11589e8204b1SProgrammingkid defer:NO]; 11599e8204b1SProgrammingkid [about_window setTitle: @"About"]; 11609e8204b1SProgrammingkid [about_window setReleasedWhenClosed: NO]; 11619e8204b1SProgrammingkid [about_window center]; 11629e8204b1SProgrammingkid NSView *superView = [about_window contentView]; 11639e8204b1SProgrammingkid 11649e8204b1SProgrammingkid /* Create the dimensions of the picture */ 11659e8204b1SProgrammingkid int picture_width = 80, picture_height = 80; 11669e8204b1SProgrammingkid x = (about_width - picture_width)/2; 11679e8204b1SProgrammingkid y = about_height - picture_height - 10; 11689e8204b1SProgrammingkid NSRect picture_rect = NSMakeRect(x, y, picture_width, picture_height); 11699e8204b1SProgrammingkid 11709e8204b1SProgrammingkid /* Get the path to the QEMU binary */ 11719e8204b1SProgrammingkid NSString *binary_name = [NSString stringWithCString: gArgv[0] 11729e8204b1SProgrammingkid encoding: NSASCIIStringEncoding]; 11739e8204b1SProgrammingkid binary_name = [binary_name lastPathComponent]; 11749e8204b1SProgrammingkid NSString *program_path = [[NSString alloc] initWithFormat: @"%@/%@", 11759e8204b1SProgrammingkid [[NSBundle mainBundle] bundlePath], binary_name]; 11769e8204b1SProgrammingkid 11779e8204b1SProgrammingkid /* Make the picture of QEMU */ 11789e8204b1SProgrammingkid NSImageView *picture_view = [[NSImageView alloc] initWithFrame: 11799e8204b1SProgrammingkid picture_rect]; 11809e8204b1SProgrammingkid NSImage *qemu_image = [[NSWorkspace sharedWorkspace] iconForFile: 11819e8204b1SProgrammingkid program_path]; 11829e8204b1SProgrammingkid [picture_view setImage: qemu_image]; 11839e8204b1SProgrammingkid [picture_view setImageScaling: NSImageScaleProportionallyUpOrDown]; 11849e8204b1SProgrammingkid [superView addSubview: picture_view]; 11859e8204b1SProgrammingkid 11869e8204b1SProgrammingkid /* Make the name label */ 11879e8204b1SProgrammingkid x = 0; 11889e8204b1SProgrammingkid y = y - 25; 11899e8204b1SProgrammingkid int name_width = about_width, name_height = 20; 11909e8204b1SProgrammingkid NSRect name_rect = NSMakeRect(x, y, name_width, name_height); 11919e8204b1SProgrammingkid NSTextField *name_label = [[NSTextField alloc] initWithFrame: name_rect]; 11929e8204b1SProgrammingkid [name_label setEditable: NO]; 11939e8204b1SProgrammingkid [name_label setBezeled: NO]; 11949e8204b1SProgrammingkid [name_label setDrawsBackground: NO]; 11959e8204b1SProgrammingkid [name_label setAlignment: NSCenterTextAlignment]; 11969e8204b1SProgrammingkid NSString *qemu_name = [[NSString alloc] initWithCString: gArgv[0] 11979e8204b1SProgrammingkid encoding: NSASCIIStringEncoding]; 11989e8204b1SProgrammingkid qemu_name = [qemu_name lastPathComponent]; 11999e8204b1SProgrammingkid [name_label setStringValue: qemu_name]; 12009e8204b1SProgrammingkid [superView addSubview: name_label]; 12019e8204b1SProgrammingkid 12029e8204b1SProgrammingkid /* Set the version label's attributes */ 12039e8204b1SProgrammingkid x = 0; 12049e8204b1SProgrammingkid y = 50; 12059e8204b1SProgrammingkid int version_width = about_width, version_height = 20; 12069e8204b1SProgrammingkid NSRect version_rect = NSMakeRect(x, y, version_width, version_height); 12079e8204b1SProgrammingkid NSTextField *version_label = [[NSTextField alloc] initWithFrame: 12089e8204b1SProgrammingkid version_rect]; 12099e8204b1SProgrammingkid [version_label setEditable: NO]; 12109e8204b1SProgrammingkid [version_label setBezeled: NO]; 12119e8204b1SProgrammingkid [version_label setAlignment: NSCenterTextAlignment]; 12129e8204b1SProgrammingkid [version_label setDrawsBackground: NO]; 12139e8204b1SProgrammingkid 12149e8204b1SProgrammingkid /* Create the version string*/ 12159e8204b1SProgrammingkid NSString *version_string; 12169e8204b1SProgrammingkid version_string = [[NSString alloc] initWithFormat: 12179e8204b1SProgrammingkid @"QEMU emulator version %s%s", QEMU_VERSION, QEMU_PKGVERSION]; 12189e8204b1SProgrammingkid [version_label setStringValue: version_string]; 12199e8204b1SProgrammingkid [superView addSubview: version_label]; 12209e8204b1SProgrammingkid 12219e8204b1SProgrammingkid /* Make copyright label */ 12229e8204b1SProgrammingkid x = 0; 12239e8204b1SProgrammingkid y = 35; 12249e8204b1SProgrammingkid int copyright_width = about_width, copyright_height = 20; 12259e8204b1SProgrammingkid NSRect copyright_rect = NSMakeRect(x, y, copyright_width, copyright_height); 12269e8204b1SProgrammingkid NSTextField *copyright_label = [[NSTextField alloc] initWithFrame: 12279e8204b1SProgrammingkid copyright_rect]; 12289e8204b1SProgrammingkid [copyright_label setEditable: NO]; 12299e8204b1SProgrammingkid [copyright_label setBezeled: NO]; 12309e8204b1SProgrammingkid [copyright_label setDrawsBackground: NO]; 12319e8204b1SProgrammingkid [copyright_label setAlignment: NSCenterTextAlignment]; 12329e8204b1SProgrammingkid [copyright_label setStringValue: [NSString stringWithFormat: @"%s", 12339e8204b1SProgrammingkid QEMU_COPYRIGHT]]; 12349e8204b1SProgrammingkid [superView addSubview: copyright_label]; 12359e8204b1SProgrammingkid} 12369e8204b1SProgrammingkid 1237b4c6a112SProgrammingkid@end 12383e230dd2SCorentin Chary 12393e230dd2SCorentin Chary 12403e230dd2SCorentin Charyint main (int argc, const char * argv[]) { 12413e230dd2SCorentin Chary 12423e230dd2SCorentin Chary gArgc = argc; 12433e230dd2SCorentin Chary gArgv = (char **)argv; 12443e230dd2SCorentin Chary int i; 12453e230dd2SCorentin Chary 12463e230dd2SCorentin Chary /* In case we don't need to display a window, let's not do that */ 12473e230dd2SCorentin Chary for (i = 1; i < argc; i++) { 1248e4ebcc1aSTristan Gingold const char *opt = argv[i]; 1249e4ebcc1aSTristan Gingold 1250e4ebcc1aSTristan Gingold if (opt[0] == '-') { 1251e4ebcc1aSTristan Gingold /* Treat --foo the same as -foo. */ 1252e4ebcc1aSTristan Gingold if (opt[1] == '-') { 1253e4ebcc1aSTristan Gingold opt++; 1254e4ebcc1aSTristan Gingold } 12559851484fSAlexandre Raymond if (!strcmp(opt, "-h") || !strcmp(opt, "-help") || 12569851484fSAlexandre Raymond !strcmp(opt, "-vnc") || 1257e4ebcc1aSTristan Gingold !strcmp(opt, "-nographic") || 1258e4ebcc1aSTristan Gingold !strcmp(opt, "-version") || 125960b46aa2SAndreas Färber !strcmp(opt, "-curses") || 1260b12a84ceSRainer Müller !strcmp(opt, "-display") || 126160b46aa2SAndreas Färber !strcmp(opt, "-qtest")) { 12623bbbee18SAndreas Färber return qemu_main(gArgc, gArgv, *_NSGetEnviron()); 12633e230dd2SCorentin Chary } 12643e230dd2SCorentin Chary } 1265e4ebcc1aSTristan Gingold } 12663e230dd2SCorentin Chary 12673e230dd2SCorentin Chary NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; 12683e230dd2SCorentin Chary 126942a5dfe7SPeter Maydell // Pull this console process up to being a fully-fledged graphical 127042a5dfe7SPeter Maydell // app with a menubar and Dock icon 127142a5dfe7SPeter Maydell ProcessSerialNumber psn = { 0, kCurrentProcess }; 127242a5dfe7SPeter Maydell TransformProcessType(&psn, kProcessTransformToForegroundApplication); 127342a5dfe7SPeter Maydell 12743e230dd2SCorentin Chary [NSApplication sharedApplication]; 12753e230dd2SCorentin Chary 12763e230dd2SCorentin Chary // Add menus 12773e230dd2SCorentin Chary NSMenu *menu; 12783e230dd2SCorentin Chary NSMenuItem *menuItem; 12793e230dd2SCorentin Chary 12803e230dd2SCorentin Chary [NSApp setMainMenu:[[NSMenu alloc] init]]; 12813e230dd2SCorentin Chary 12823e230dd2SCorentin Chary // Application menu 12833e230dd2SCorentin Chary menu = [[NSMenu alloc] initWithTitle:@""]; 12849e8204b1SProgrammingkid [menu addItemWithTitle:@"About QEMU" action:@selector(do_about_menu_item:) keyEquivalent:@""]; // About QEMU 12853e230dd2SCorentin Chary [menu addItem:[NSMenuItem separatorItem]]; //Separator 12863e230dd2SCorentin Chary [menu addItemWithTitle:@"Hide QEMU" action:@selector(hide:) keyEquivalent:@"h"]; //Hide QEMU 12873e230dd2SCorentin Chary menuItem = (NSMenuItem *)[menu addItemWithTitle:@"Hide Others" action:@selector(hideOtherApplications:) keyEquivalent:@"h"]; // Hide Others 12883e230dd2SCorentin Chary [menuItem setKeyEquivalentModifierMask:(NSAlternateKeyMask|NSCommandKeyMask)]; 12893e230dd2SCorentin Chary [menu addItemWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""]; // Show All 12903e230dd2SCorentin Chary [menu addItem:[NSMenuItem separatorItem]]; //Separator 12913e230dd2SCorentin Chary [menu addItemWithTitle:@"Quit QEMU" action:@selector(terminate:) keyEquivalent:@"q"]; 12923e230dd2SCorentin Chary menuItem = [[NSMenuItem alloc] initWithTitle:@"Apple" action:nil keyEquivalent:@""]; 12933e230dd2SCorentin Chary [menuItem setSubmenu:menu]; 12943e230dd2SCorentin Chary [[NSApp mainMenu] addItem:menuItem]; 12953e230dd2SCorentin Chary [NSApp performSelector:@selector(setAppleMenu:) withObject:menu]; // Workaround (this method is private since 10.4+) 12963e230dd2SCorentin Chary 12978524f1c7SJohn Arbuckle // Machine menu 12988524f1c7SJohn Arbuckle menu = [[NSMenu alloc] initWithTitle: @"Machine"]; 12998524f1c7SJohn Arbuckle [menu setAutoenablesItems: NO]; 13008524f1c7SJohn Arbuckle [menu addItem: [[[NSMenuItem alloc] initWithTitle: @"Pause" action: @selector(pauseQEMU:) keyEquivalent: @""] autorelease]]; 13018524f1c7SJohn Arbuckle menuItem = [[[NSMenuItem alloc] initWithTitle: @"Resume" action: @selector(resumeQEMU:) keyEquivalent: @""] autorelease]; 13028524f1c7SJohn Arbuckle [menu addItem: menuItem]; 13038524f1c7SJohn Arbuckle [menuItem setEnabled: NO]; 130427074614SJohn Arbuckle [menu addItem: [NSMenuItem separatorItem]]; 130527074614SJohn Arbuckle [menu addItem: [[[NSMenuItem alloc] initWithTitle: @"Reset" action: @selector(restartQEMU:) keyEquivalent: @""] autorelease]]; 130627074614SJohn Arbuckle [menu addItem: [[[NSMenuItem alloc] initWithTitle: @"Power Down" action: @selector(powerDownQEMU:) keyEquivalent: @""] autorelease]]; 13078524f1c7SJohn Arbuckle menuItem = [[[NSMenuItem alloc] initWithTitle: @"Machine" action:nil keyEquivalent:@""] autorelease]; 13088524f1c7SJohn Arbuckle [menuItem setSubmenu:menu]; 13098524f1c7SJohn Arbuckle [[NSApp mainMenu] addItem:menuItem]; 13108524f1c7SJohn Arbuckle 13113e230dd2SCorentin Chary // View menu 13123e230dd2SCorentin Chary menu = [[NSMenu alloc] initWithTitle:@"View"]; 13135d1b2eefSProgrammingkid [menu addItem: [[[NSMenuItem alloc] initWithTitle:@"Enter Fullscreen" action:@selector(doToggleFullScreen:) keyEquivalent:@"f"] autorelease]]; // Fullscreen 13145d1b2eefSProgrammingkid [menu addItem: [[[NSMenuItem alloc] initWithTitle:@"Zoom To Fit" action:@selector(zoomToFit:) keyEquivalent:@""] autorelease]]; 13153e230dd2SCorentin Chary menuItem = [[[NSMenuItem alloc] initWithTitle:@"View" action:nil keyEquivalent:@""] autorelease]; 13163e230dd2SCorentin Chary [menuItem setSubmenu:menu]; 13173e230dd2SCorentin Chary [[NSApp mainMenu] addItem:menuItem]; 13183e230dd2SCorentin Chary 13193e230dd2SCorentin Chary // Window menu 13203e230dd2SCorentin Chary menu = [[NSMenu alloc] initWithTitle:@"Window"]; 13213e230dd2SCorentin Chary [menu addItem: [[[NSMenuItem alloc] initWithTitle:@"Minimize" action:@selector(performMiniaturize:) keyEquivalent:@"m"] autorelease]]; // Miniaturize 13223e230dd2SCorentin Chary menuItem = [[[NSMenuItem alloc] initWithTitle:@"Window" action:nil keyEquivalent:@""] autorelease]; 13233e230dd2SCorentin Chary [menuItem setSubmenu:menu]; 13243e230dd2SCorentin Chary [[NSApp mainMenu] addItem:menuItem]; 13253e230dd2SCorentin Chary [NSApp setWindowsMenu:menu]; 13263e230dd2SCorentin Chary 13273e230dd2SCorentin Chary // Help menu 13283e230dd2SCorentin Chary menu = [[NSMenu alloc] initWithTitle:@"Help"]; 13293e230dd2SCorentin Chary [menu addItem: [[[NSMenuItem alloc] initWithTitle:@"QEMU Documentation" action:@selector(showQEMUDoc:) keyEquivalent:@"?"] autorelease]]; // QEMU Help 13303e230dd2SCorentin Chary menuItem = [[[NSMenuItem alloc] initWithTitle:@"Window" action:nil keyEquivalent:@""] autorelease]; 13313e230dd2SCorentin Chary [menuItem setSubmenu:menu]; 13323e230dd2SCorentin Chary [[NSApp mainMenu] addItem:menuItem]; 13333e230dd2SCorentin Chary 13343e230dd2SCorentin Chary // Create an Application controller 13353e230dd2SCorentin Chary QemuCocoaAppController *appController = [[QemuCocoaAppController alloc] init]; 13363e230dd2SCorentin Chary [NSApp setDelegate:appController]; 13373e230dd2SCorentin Chary 13383e230dd2SCorentin Chary // Start the main event loop 13393e230dd2SCorentin Chary [NSApp run]; 13403e230dd2SCorentin Chary 13413e230dd2SCorentin Chary [appController release]; 13423e230dd2SCorentin Chary [pool release]; 13433e230dd2SCorentin Chary 13443e230dd2SCorentin Chary return 0; 13453e230dd2SCorentin Chary} 13463e230dd2SCorentin Chary 13473e230dd2SCorentin Chary 13483e230dd2SCorentin Chary 13493e230dd2SCorentin Chary#pragma mark qemu 13507c20b4a3SGerd Hoffmannstatic void cocoa_update(DisplayChangeListener *dcl, 13517c20b4a3SGerd Hoffmann int x, int y, int w, int h) 13523e230dd2SCorentin Chary{ 13536e657e64SPeter Maydell NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; 13546e657e64SPeter Maydell 13553e230dd2SCorentin Chary COCOA_DEBUG("qemu_cocoa: cocoa_update\n"); 13563e230dd2SCorentin Chary 13573e230dd2SCorentin Chary NSRect rect; 13583e230dd2SCorentin Chary if ([cocoaView cdx] == 1.0) { 13593e230dd2SCorentin Chary rect = NSMakeRect(x, [cocoaView gscreen].height - y - h, w, h); 13603e230dd2SCorentin Chary } else { 13613e230dd2SCorentin Chary rect = NSMakeRect( 13623e230dd2SCorentin Chary x * [cocoaView cdx], 13633e230dd2SCorentin Chary ([cocoaView gscreen].height - y - h) * [cocoaView cdy], 13643e230dd2SCorentin Chary w * [cocoaView cdx], 13653e230dd2SCorentin Chary h * [cocoaView cdy]); 13663e230dd2SCorentin Chary } 13673e230dd2SCorentin Chary [cocoaView setNeedsDisplayInRect:rect]; 13686e657e64SPeter Maydell 13696e657e64SPeter Maydell [pool release]; 13703e230dd2SCorentin Chary} 13713e230dd2SCorentin Chary 1372c12aeb86SGerd Hoffmannstatic void cocoa_switch(DisplayChangeListener *dcl, 1373c12aeb86SGerd Hoffmann DisplaySurface *surface) 13743e230dd2SCorentin Chary{ 13756e657e64SPeter Maydell NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; 13763e230dd2SCorentin Chary 13776e657e64SPeter Maydell COCOA_DEBUG("qemu_cocoa: cocoa_switch\n"); 13785e00d3acSGerd Hoffmann [cocoaView switchSurface:surface]; 13796e657e64SPeter Maydell [pool release]; 13803e230dd2SCorentin Chary} 13813e230dd2SCorentin Chary 1382bc2ed970SGerd Hoffmannstatic void cocoa_refresh(DisplayChangeListener *dcl) 13833e230dd2SCorentin Chary{ 13846e657e64SPeter Maydell NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; 13856e657e64SPeter Maydell 13863e230dd2SCorentin Chary COCOA_DEBUG("qemu_cocoa: cocoa_refresh\n"); 1387468a895bSJohn Arbuckle graphic_hw_update(NULL); 13883e230dd2SCorentin Chary 138921bae11aSGerd Hoffmann if (qemu_input_is_absolute()) { 13903e230dd2SCorentin Chary if (![cocoaView isAbsoluteEnabled]) { 139149b9bd4dSPeter Maydell if ([cocoaView isMouseGrabbed]) { 13923e230dd2SCorentin Chary [cocoaView ungrabMouse]; 13933e230dd2SCorentin Chary } 13943e230dd2SCorentin Chary } 13953e230dd2SCorentin Chary [cocoaView setAbsoluteEnabled:YES]; 13963e230dd2SCorentin Chary } 13973e230dd2SCorentin Chary 13983e230dd2SCorentin Chary NSDate *distantPast; 13993e230dd2SCorentin Chary NSEvent *event; 14003e230dd2SCorentin Chary distantPast = [NSDate distantPast]; 14013e230dd2SCorentin Chary do { 14023e230dd2SCorentin Chary event = [NSApp nextEventMatchingMask:NSAnyEventMask untilDate:distantPast 14033e230dd2SCorentin Chary inMode: NSDefaultRunLoopMode dequeue:YES]; 14043e230dd2SCorentin Chary if (event != nil) { 14053e230dd2SCorentin Chary [cocoaView handleEvent:event]; 14063e230dd2SCorentin Chary } 14073e230dd2SCorentin Chary } while(event != nil); 14086e657e64SPeter Maydell [pool release]; 14093e230dd2SCorentin Chary} 14103e230dd2SCorentin Chary 14113e230dd2SCorentin Charystatic void cocoa_cleanup(void) 14123e230dd2SCorentin Chary{ 14133e230dd2SCorentin Chary COCOA_DEBUG("qemu_cocoa: cocoa_cleanup\n"); 141458a06675SBlue Swirl g_free(dcl); 14153e230dd2SCorentin Chary} 14163e230dd2SCorentin Chary 14177c20b4a3SGerd Hoffmannstatic const DisplayChangeListenerOps dcl_ops = { 14187c20b4a3SGerd Hoffmann .dpy_name = "cocoa", 14198510d91eSPeter Maydell .dpy_gfx_update = cocoa_update, 14208510d91eSPeter Maydell .dpy_gfx_switch = cocoa_switch, 14218510d91eSPeter Maydell .dpy_refresh = cocoa_refresh, 14227c20b4a3SGerd Hoffmann}; 14237c20b4a3SGerd Hoffmann 1424b4c6a112SProgrammingkid/* Returns a name for a given console */ 1425b4c6a112SProgrammingkidstatic NSString * getConsoleName(QemuConsole * console) 1426b4c6a112SProgrammingkid{ 1427b4c6a112SProgrammingkid return [NSString stringWithFormat: @"%s", qemu_console_get_label(console)]; 1428b4c6a112SProgrammingkid} 1429b4c6a112SProgrammingkid 1430b4c6a112SProgrammingkid/* Add an entry to the View menu for each console */ 1431b4c6a112SProgrammingkidstatic void add_console_menu_entries(void) 1432b4c6a112SProgrammingkid{ 1433b4c6a112SProgrammingkid NSMenu *menu; 1434b4c6a112SProgrammingkid NSMenuItem *menuItem; 1435b4c6a112SProgrammingkid int index = 0; 1436b4c6a112SProgrammingkid 1437b4c6a112SProgrammingkid menu = [[[NSApp mainMenu] itemWithTitle:@"View"] submenu]; 1438b4c6a112SProgrammingkid 1439b4c6a112SProgrammingkid [menu addItem:[NSMenuItem separatorItem]]; 1440b4c6a112SProgrammingkid 1441b4c6a112SProgrammingkid while (qemu_console_lookup_by_index(index) != NULL) { 1442b4c6a112SProgrammingkid menuItem = [[[NSMenuItem alloc] initWithTitle: getConsoleName(qemu_console_lookup_by_index(index)) 1443b4c6a112SProgrammingkid action: @selector(displayConsole:) keyEquivalent: @""] autorelease]; 1444b4c6a112SProgrammingkid [menuItem setTag: index]; 1445b4c6a112SProgrammingkid [menu addItem: menuItem]; 1446b4c6a112SProgrammingkid index++; 1447b4c6a112SProgrammingkid } 1448b4c6a112SProgrammingkid} 1449b4c6a112SProgrammingkid 1450693a3e01SJohn Arbuckle/* Make menu items for all removable devices. 1451693a3e01SJohn Arbuckle * Each device is given an 'Eject' and 'Change' menu item. 1452693a3e01SJohn Arbuckle */ 1453a7940ec0SJohn Arbucklestatic void addRemovableDevicesMenuItems(void) 1454693a3e01SJohn Arbuckle{ 1455693a3e01SJohn Arbuckle NSMenu *menu; 1456693a3e01SJohn Arbuckle NSMenuItem *menuItem; 1457693a3e01SJohn Arbuckle BlockInfoList *currentDevice, *pointerToFree; 1458693a3e01SJohn Arbuckle NSString *deviceName; 1459693a3e01SJohn Arbuckle 1460693a3e01SJohn Arbuckle currentDevice = qmp_query_block(NULL); 1461693a3e01SJohn Arbuckle pointerToFree = currentDevice; 1462693a3e01SJohn Arbuckle if(currentDevice == NULL) { 1463693a3e01SJohn Arbuckle NSBeep(); 1464693a3e01SJohn Arbuckle QEMU_Alert(@"Failed to query for block devices!"); 1465693a3e01SJohn Arbuckle return; 1466693a3e01SJohn Arbuckle } 1467693a3e01SJohn Arbuckle 1468693a3e01SJohn Arbuckle menu = [[[NSApp mainMenu] itemWithTitle:@"Machine"] submenu]; 1469693a3e01SJohn Arbuckle 1470693a3e01SJohn Arbuckle // Add a separator between related groups of menu items 1471693a3e01SJohn Arbuckle [menu addItem:[NSMenuItem separatorItem]]; 1472693a3e01SJohn Arbuckle 1473693a3e01SJohn Arbuckle // Set the attributes to the "Removable Media" menu item 1474693a3e01SJohn Arbuckle NSString *titleString = @"Removable Media"; 1475693a3e01SJohn Arbuckle NSMutableAttributedString *attString=[[NSMutableAttributedString alloc] initWithString:titleString]; 1476693a3e01SJohn Arbuckle NSColor *newColor = [NSColor blackColor]; 1477693a3e01SJohn Arbuckle NSFontManager *fontManager = [NSFontManager sharedFontManager]; 1478693a3e01SJohn Arbuckle NSFont *font = [fontManager fontWithFamily:@"Helvetica" 1479693a3e01SJohn Arbuckle traits:NSBoldFontMask|NSItalicFontMask 1480693a3e01SJohn Arbuckle weight:0 1481693a3e01SJohn Arbuckle size:14]; 1482693a3e01SJohn Arbuckle [attString addAttribute:NSFontAttributeName value:font range:NSMakeRange(0, [titleString length])]; 1483693a3e01SJohn Arbuckle [attString addAttribute:NSForegroundColorAttributeName value:newColor range:NSMakeRange(0, [titleString length])]; 1484693a3e01SJohn Arbuckle [attString addAttribute:NSUnderlineStyleAttributeName value:[NSNumber numberWithInt: 1] range:NSMakeRange(0, [titleString length])]; 1485693a3e01SJohn Arbuckle 1486693a3e01SJohn Arbuckle // Add the "Removable Media" menu item 1487693a3e01SJohn Arbuckle menuItem = [NSMenuItem new]; 1488693a3e01SJohn Arbuckle [menuItem setAttributedTitle: attString]; 1489693a3e01SJohn Arbuckle [menuItem setEnabled: NO]; 1490693a3e01SJohn Arbuckle [menu addItem: menuItem]; 1491693a3e01SJohn Arbuckle 1492cb8d4c8fSStefan Weil /* Loop through all the block devices in the emulator */ 1493693a3e01SJohn Arbuckle while (currentDevice) { 1494693a3e01SJohn Arbuckle deviceName = [[NSString stringWithFormat: @"%s", currentDevice->value->device] retain]; 1495693a3e01SJohn Arbuckle 1496693a3e01SJohn Arbuckle if(currentDevice->value->removable) { 1497693a3e01SJohn Arbuckle menuItem = [[NSMenuItem alloc] initWithTitle: [NSString stringWithFormat: @"Change %s...", currentDevice->value->device] 1498693a3e01SJohn Arbuckle action: @selector(changeDeviceMedia:) 1499693a3e01SJohn Arbuckle keyEquivalent: @""]; 1500693a3e01SJohn Arbuckle [menu addItem: menuItem]; 1501693a3e01SJohn Arbuckle [menuItem setRepresentedObject: deviceName]; 1502693a3e01SJohn Arbuckle [menuItem autorelease]; 1503693a3e01SJohn Arbuckle 1504693a3e01SJohn Arbuckle menuItem = [[NSMenuItem alloc] initWithTitle: [NSString stringWithFormat: @"Eject %s", currentDevice->value->device] 1505693a3e01SJohn Arbuckle action: @selector(ejectDeviceMedia:) 1506693a3e01SJohn Arbuckle keyEquivalent: @""]; 1507693a3e01SJohn Arbuckle [menu addItem: menuItem]; 1508693a3e01SJohn Arbuckle [menuItem setRepresentedObject: deviceName]; 1509693a3e01SJohn Arbuckle [menuItem autorelease]; 1510693a3e01SJohn Arbuckle } 1511693a3e01SJohn Arbuckle currentDevice = currentDevice->next; 1512693a3e01SJohn Arbuckle } 1513693a3e01SJohn Arbuckle qapi_free_BlockInfoList(pointerToFree); 1514693a3e01SJohn Arbuckle} 1515693a3e01SJohn Arbuckle 15163e230dd2SCorentin Charyvoid cocoa_display_init(DisplayState *ds, int full_screen) 15173e230dd2SCorentin Chary{ 15183e230dd2SCorentin Chary COCOA_DEBUG("qemu_cocoa: cocoa_display_init\n"); 15193e230dd2SCorentin Chary 152043227af8SProgrammingkid /* if fullscreen mode is to be used */ 152143227af8SProgrammingkid if (full_screen == true) { 152243227af8SProgrammingkid [NSApp activateIgnoringOtherApps: YES]; 152343227af8SProgrammingkid [(QemuCocoaAppController *)[[NSApplication sharedApplication] delegate] toggleFullScreen: nil]; 152443227af8SProgrammingkid } 152543227af8SProgrammingkid 152658a06675SBlue Swirl dcl = g_malloc0(sizeof(DisplayChangeListener)); 15273e230dd2SCorentin Chary 15283e230dd2SCorentin Chary // register vga output callbacks 15297c20b4a3SGerd Hoffmann dcl->ops = &dcl_ops; 15305209089fSGerd Hoffmann register_displaychangelistener(dcl); 15313e230dd2SCorentin Chary 15323e230dd2SCorentin Chary // register cleanup function 15333e230dd2SCorentin Chary atexit(cocoa_cleanup); 1534b4c6a112SProgrammingkid 1535b4c6a112SProgrammingkid /* At this point QEMU has created all the consoles, so we can add View 1536b4c6a112SProgrammingkid * menu entries for them. 1537b4c6a112SProgrammingkid */ 1538b4c6a112SProgrammingkid add_console_menu_entries(); 1539693a3e01SJohn Arbuckle 1540693a3e01SJohn Arbuckle /* Give all removable devices a menu item. 1541693a3e01SJohn Arbuckle * Has to be called after QEMU has started to 1542693a3e01SJohn Arbuckle * find out what removable devices it has. 1543693a3e01SJohn Arbuckle */ 1544693a3e01SJohn Arbuckle addRemovableDevicesMenuItems(); 15453e230dd2SCorentin Chary} 1546