/* ** ClanLib SDK ** Copyright (c) 1997-2005 The ClanLib Team ** ** This software is provided 'as-is', without any express or implied ** warranty. In no event will the authors be held liable for any damages ** arising from the use of this software. ** ** Permission is granted to anyone to use this software for any purpose, ** including commercial applications, and to alter it and redistribute it ** freely, subject to the following restrictions: ** ** 1. The origin of this software must not be misrepresented; you must not ** claim that you wrote the original software. If you use this software ** in a product, an acknowledgment in the product documentation would be ** appreciated but is not required. ** 2. Altered source versions must be plainly marked as such, and must not be ** misrepresented as being the original software. ** 3. This notice may not be removed or altered from any source distribution. ** ** Note: Some of the libraries ClanLib may link to may have additional ** requirements or restrictions. ** ** File Author(s): ** ** Magnus Norddahl ** (if your name is missing here, please add it) */ #include #include #include #include "display_window_opengl.h" #include "Display/display_precomp.h" #include "Display/display_window_generic.h" #include "API/Core/System/keep_alive.h" #include "../graphic_context_opengl.h" #include "API/Core/System/error.h" #include "API/Core/System/log.h" #include "GL/gl_state_checker.h" #include "GL/AGL/input_device_mackeyboard.h" #include "GL/AGL/input_device_macmouse.h" #include "API/Core/System/clanstring.h" #include "API/GL/opengl_window_description.h" #include "API/GL/opengl_state.h" #include "API/GL/opengl_wrap.h" #include "API/Display/keys.h" #include "API/Display/input_device.h" #include "API/Display/input_event.h" #include "../opengl_window_description_generic.h" #include AGLContext CL_DisplayWindow_OpenGL::share_context = 0; int CL_DisplayWindow_OpenGL::disp_ref_count = 0; ///////////////////////////////////////////////////////////////////////////// // Construction: CL_DisplayWindow_OpenGL::CL_DisplayWindow_OpenGL() : left_ctrl_down(false), left_alt_down(false), left_shift_down(false), right_ctrl_down(false), right_alt_down(false), right_shift_down(false), fullscreen(false), fullscreen_width(0), fullscreen_height(0), saved_position(0, 0, 0, 0), context(0), win_context(0), fs_context(0), window_ref(0) { static bool first_call = true; if (first_call) { // Enable key-up: SetEventMask(everyEvent); target_ref = GetEventDispatcherTarget(); first_call = false; } gc = CL_GraphicContext(new CL_GraphicContext_OpenGL(this)); } CL_DisplayWindow_OpenGL::~CL_DisplayWindow_OpenGL() { // if(fullscreen) // set_windowed(); destroy_window(); } ///////////////////////////////////////////////////////////////////////////// // Attributes: int CL_DisplayWindow_OpenGL::get_width() const { Rect rect; GetWindowPortBounds(window_ref, &rect); return rect.right-rect.left; } int CL_DisplayWindow_OpenGL::get_height() const { Rect rect; GetWindowPortBounds(window_ref, &rect); return rect.bottom-rect.top; } bool CL_DisplayWindow_OpenGL::is_fullscreen() const { return fullscreen; } bool CL_DisplayWindow_OpenGL::has_focus() const { return focus; } CL_PixelBuffer CL_DisplayWindow_OpenGL::get_buffer(int i) const { if (i == 0) { return gc.get_pixeldata(CL_Rect(0, 0, get_width(), get_height()), CL_FRONT); } else { return gc.get_pixeldata(CL_Rect(0, 0, get_width(), get_height()), CL_BACK); } } int CL_DisplayWindow_OpenGL::get_buffer_count() const { return 2; } CL_GraphicContext *CL_DisplayWindow_OpenGL::get_gc() { return &gc; } CL_Size CL_DisplayWindow_OpenGL::get_max_surface_size() const { CLint max_texture_size; clGetIntegerv(CL_MAX_TEXTURE_SIZE, &max_texture_size); return CL_Size(max_texture_size, max_texture_size); } ///////////////////////////////////////////////////////////////////////////// // Operations: void CL_DisplayWindow_OpenGL::create_window(const CL_DisplayWindowDescription &desc) { OSStatus result; const CL_OpenGLWindowDescription_Generic *gl_desc = 0; gl_desc = dynamic_cast(desc.impl.get()); fullscreen_width = desc.get_size().width; fullscreen_height = desc.get_size().height; disp_ref_count++; GLint gl_attribs_single[] = { AGL_RGBA, AGL_RED_SIZE, 4, AGL_GREEN_SIZE, 4, AGL_BLUE_SIZE, 4, AGL_DEPTH_SIZE, 16, AGL_NONE }; GLint gl_attribs[32]; int i = 0; if( gl_desc ) { if( gl_desc->rgba ) gl_attribs[i++] = AGL_RGBA; if( gl_desc->doublebuffer ) gl_attribs[i++] = AGL_DOUBLEBUFFER; //if( gl_desc->stereo ) gl_attribs[i++] = AGL_STEREO; gl_attribs[i++] = AGL_BUFFER_SIZE; gl_attribs[i++] = gl_desc->buffer_size; gl_attribs[i++] = AGL_LEVEL; gl_attribs[i++] = gl_desc->level; gl_attribs[i++] = AGL_AUX_BUFFERS; gl_attribs[i++] = gl_desc->aux_buffers; gl_attribs[i++] = AGL_RED_SIZE; gl_attribs[i++] = gl_desc->red_size; gl_attribs[i++] = AGL_GREEN_SIZE; gl_attribs[i++] = gl_desc->green_size; gl_attribs[i++] = AGL_BLUE_SIZE; gl_attribs[i++] = gl_desc->blue_size; gl_attribs[i++] = AGL_DEPTH_SIZE; gl_attribs[i++] = gl_desc->depth_size; gl_attribs[i++] = AGL_STENCIL_SIZE; gl_attribs[i++] = gl_desc->stencil_size; gl_attribs[i++] = AGL_ACCUM_RED_SIZE; gl_attribs[i++] = gl_desc->accum_red_size; gl_attribs[i++] = AGL_ACCUM_GREEN_SIZE; gl_attribs[i++] = gl_desc->accum_green_size; gl_attribs[i++] = AGL_ACCUM_BLUE_SIZE; gl_attribs[i++] = gl_desc->accum_blue_size; gl_attribs[i++] = AGL_ACCUM_ALPHA_SIZE; gl_attribs[i++] = gl_desc->accum_alpha_size; gl_attribs[i++] = AGL_ACCUM_RED_SIZE; gl_attribs[i++] = gl_desc->accum_red_size; // gl_attribs[i++] = AGL_FULLSCREEN; } else { gl_attribs[i++] = AGL_RGBA; gl_attribs[i++] = AGL_NO_RECOVERY; gl_attribs[i++] = AGL_DOUBLEBUFFER; gl_attribs[i++] = AGL_DEPTH_SIZE; gl_attribs[i++] = 16; } /* else { gl_attribs[i++] = AGL_RGBA; gl_attribs[i++] = AGL_DOUBLEBUFFER; gl_attribs[i++] = AGL_RED_SIZE; gl_attribs[i++] = 4; gl_attribs[i++] = AGL_GREEN_SIZE; gl_attribs[i++] = 4; gl_attribs[i++] = AGL_BLUE_SIZE; gl_attribs[i++] = 4; gl_attribs[i++] = AGL_DEPTH_SIZE; gl_attribs[i++] = 16; // gl_attribs[i++] = AGL_FULLSCREEN; } */ gl_attribs[i] = AGL_NONE; CGDirectDisplayID display = CGMainDisplayID(); GDHandle gdhDisplay, *pgdhDisplay; int numDisplay; pgdhDisplay = &gdhDisplay; if (noErr == DMGetGDeviceByDisplayID ((DisplayIDType)display, pgdhDisplay, false)) numDisplay = 1; else { pgdhDisplay = 0; numDisplay = 0; } AGLPixelFormat pixelformat; pixelformat = aglChoosePixelFormat(pgdhDisplay, numDisplay, gl_attribs); win_context = aglCreateContext(pixelformat, share_context); if (!share_context) share_context = win_context; aglDestroyPixelFormat(pixelformat); gl_attribs[i++] = AGL_FULLSCREEN; gl_attribs[i] = AGL_NONE; pixelformat = aglChoosePixelFormat(pgdhDisplay, numDisplay, gl_attribs); fs_context = aglCreateContext(pixelformat, win_context); aglDestroyPixelFormat(pixelformat); if (!(win_context && fs_context)) { printf("Requested visual not supported by your OpenGL implementation. Falling back on singlebuffered Visual!\n"); pixelformat = aglChoosePixelFormat(0, 0, gl_attribs_single); win_context = aglCreateContext(pixelformat, share_context); aglDestroyPixelFormat(pixelformat); fs_context = 0; } WindowAttributes style = kWindowCloseBoxAttribute | kWindowStandardHandlerAttribute; if (desc.get_allow_resize()) style |= kWindowResizableAttribute | kWindowFullZoomAttribute; Rect window_rect; SetRect(&window_rect, 50, 50, 50+desc.get_size().width, 50+desc.get_size().height); result = CreateNewWindow(kDocumentWindowClass, style, &window_rect, &window_ref); if (result != noErr) printf("Could not create window, due to error %d\n", (int)result); // set title of window: set_title(desc.get_title()); // Set standard arrow cursor: InitCursor(); // do we need to do this? -- iMBN, 13. may 2004 // Create input devices for window: keyboard = CL_InputDevice(new CL_InputDevice_MacKeyboard(this)); mouse = CL_InputDevice(new CL_InputDevice_MacMouse(this)); get_ic()->clear(); get_ic()->add_keyboard(keyboard); get_ic()->add_mouse(mouse); // buffer_front = CL_PixelBuffer(new CL_PixelBuffer_OpenGL_Frame(CL_FRONT, gc)); // buffer_back = CL_PixelBuffer(new CL_PixelBuffer_OpenGL_Frame(CL_BACK, gc)); if (!aglSetDrawable(win_context, GetWindowPort(window_ref))) { printf("Unable to set drawable"); } if (desc.is_fullscreen()) set_fullscreen(desc.get_size().width, desc.get_size().height, desc.get_bpp(), desc.get_refresh_rate()); else { fullscreen = true; // not really, but need to fool set_windowed set_windowed(); } ShowWindow(window_ref); } void CL_DisplayWindow_OpenGL::install_event_handler(EventTargetRef targ_ref, int remove_only) { static EventHandlerUPP window_handler_proc = 0; if (window_handler_proc == 0) window_handler_proc = NewEventHandlerUPP(&CL_DisplayWindow_OpenGL::on_window_event); static EventHandlerRef current_handler = 0; if (current_handler != 0) RemoveEventHandler(current_handler); if (remove_only) return; EventTypeSpec events[20]; events[0].eventClass = kEventClassKeyboard; events[0].eventKind = kEventRawKeyDown; events[1].eventClass = kEventClassKeyboard; events[1].eventKind = kEventRawKeyRepeat; events[2].eventClass = kEventClassKeyboard; events[2].eventKind = kEventRawKeyUp; events[3].eventClass = kEventClassMouse; events[3].eventKind = kEventMouseDown; events[4].eventClass = kEventClassMouse; events[4].eventKind = kEventMouseUp; events[5].eventClass = kEventClassMouse; events[5].eventKind = kEventMouseMoved; events[6].eventClass = kEventClassMouse; events[6].eventKind = kEventMouseWheelMoved; events[7].eventClass = kEventClassWindow; events[7].eventKind = kEventWindowCollapsing; events[8].eventClass = kEventClassWindow; events[8].eventKind = kEventWindowDrawContent; events[9].eventClass = kEventClassWindow; events[9].eventKind = kEventWindowShown; events[10].eventClass = kEventClassWindow; events[10].eventKind = kEventWindowClose; events[11].eventClass = kEventClassWindow; events[11].eventKind = kEventWindowActivated; events[12].eventClass = kEventClassWindow; events[12].eventKind = kEventWindowDeactivated; events[13].eventClass = kEventClassWindow; events[13].eventKind = kEventWindowBoundsChanged; events[14].eventClass = kEventClassWindow; events[14].eventKind = kEventWindowZoomed; events[15].eventClass = kEventClassWindow; events[15].eventKind = kEventWindowFocusAcquired; events[16].eventClass = kEventClassWindow; events[16].eventKind = kEventWindowFocusRelinquish; events[17].eventClass = kEventClassMouse; events[17].eventKind = kEventMouseDragged; events[18].eventClass = kEventClassKeyboard; events[18].eventKind = kEventRawKeyModifiersChanged; InstallEventHandler(targ_ref, window_handler_proc, 19, events, this, ¤t_handler); } void CL_DisplayWindow_OpenGL::destroy_window() { install_event_handler(0, 1); if (context) aglSetCurrentContext(0); if (fs_context) { aglDestroyContext(fs_context); fs_context = 0; } if (win_context) { aglDestroyContext(win_context); win_context = 0; } keyboard = CL_InputDevice(); mouse = CL_InputDevice(); get_ic()->clear(); if (window_ref) ReleaseWindow(window_ref); window_ref = 0; } void CL_DisplayWindow_OpenGL::flip(int interval) { CL_GLStateChecker::from_gc(get_gc())->flush(); if (interval != -1) { GLint param = interval; aglSetInteger(context, AGL_SWAP_INTERVAL, ¶m); } aglSwapBuffers(context); } void CL_DisplayWindow_OpenGL::set_fullscreen(int width, int height, int bpp, int refresh_rate) { if(fullscreen) return; CGCaptureAllDisplays(); aglSetCurrentContext(fs_context); aglSetFullScreen(fs_context, width, height, refresh_rate, 0); context = fs_context; install_event_handler(GetApplicationEventTarget()); fullscreen = true; focus = true; //After toggling to the fullscreen context, nothing is going draw unless //we reapply the GL states/properties -mrfun 6-2-2006 CL_GLStateChecker::from_gc(get_gc())->reinitialize_asap(); } void CL_DisplayWindow_OpenGL::set_windowed() { if(!fullscreen) return; if (context == fs_context) { aglSetDrawable(fs_context, 0); } CGReleaseAllDisplays(); aglSetCurrentContext(win_context); aglUpdateContext(win_context); install_event_handler(GetWindowEventTarget(window_ref)); fullscreen = false; context = win_context; focus = IsWindowHilited(window_ref); //After toggling to the fullscreen context, nothing is going draw unless //we reapply the GL states/properties -mrfun 6-2-2006 CL_GLStateChecker::from_gc(get_gc())->reinitialize_asap(); } void CL_DisplayWindow_OpenGL::set_title(const std::string &title) { CFStringRef s = CFStringCreateWithCString(0, title.c_str(), kCFStringEncodingISOLatin1); SetWindowTitleWithCFString(window_ref, s); CFRelease(s); } void CL_DisplayWindow_OpenGL::set_position(const CL_Rect &pos) { set_position(pos.left, pos.top); set_size(pos.get_width(), pos.get_height()); } void CL_DisplayWindow_OpenGL::set_position(int x, int y) { MoveWindow(window_ref, x, y, false); } void CL_DisplayWindow_OpenGL::set_size(int width, int height) { fullscreen_width = width; fullscreen_height = height; SizeWindow(window_ref, width, height, true); sig_resize(width, height); } void CL_DisplayWindow_OpenGL::set_buffer_count(int flipping_buffers) { // Not implemented. // We can't change how many buffer we use in OpenGL, the drivers pick for us CL_Log::log("debug", "CL_DisplayWindow_OpenGL::set_buffer_count is not implemented"); } void CL_DisplayWindow_OpenGL::update(const CL_Rect &rect) { CL_OpenGLState state(get_gc()); state.set_active(); state.setup_2d(); clReadBuffer(CL_BACK); if(clGetError() == CL_NO_ERROR) // Check if we got more than 1 buffer. { float raster_pos[4]; clGetFloatv(CL_CURRENT_RASTER_POSITION, raster_pos); clDrawBuffer(CL_FRONT); clRasterPos3f(rect.left, rect.bottom, raster_pos[2]); clCopyPixels(rect.top, rect.left, rect.get_width(), rect.get_height(), CL_COLOR); // Return to drawing to the back buffer clDrawBuffer(CL_BACK); } } void CL_DisplayWindow_OpenGL::show_system_cursor() { ShowCursor(); } void CL_DisplayWindow_OpenGL::hide_system_cursor() { // Should be replaced by a function only hiding it over this window, if possible. HideCursor(); } void CL_DisplayWindow_OpenGL::cursor_changed() { } void CL_DisplayWindow_OpenGL::keep_alive() { EventRef theEvent; while(ReceiveNextEvent(0, NULL,kEventDurationNoWait,true,&theEvent)== noErr) { SendEventToEventTarget (theEvent, target_ref); ReleaseEvent(theEvent); } } int CL_DisplayWindow_OpenGL::clkey_to_keycode(int clkey) { switch (clkey) { case CL_KEY_A: return 0; case CL_KEY_S: return 1; case CL_KEY_D: return 2; case CL_KEY_F: return 3; case CL_KEY_H: return 4; case CL_KEY_G: return 5; case CL_KEY_Z: return 6; case CL_KEY_X: return 7; case CL_KEY_C: return 8; case CL_KEY_V: return 9; case CL_KEY_B: return 11; case CL_KEY_Q: return 12; case CL_KEY_W: return 13; case CL_KEY_E: return 14; case CL_KEY_R: return 15; case CL_KEY_Y: return 16; case CL_KEY_T: return 17; case CL_KEY_1: return 18; case CL_KEY_2: return 19; case CL_KEY_3: return 20; case CL_KEY_4: return 21; case CL_KEY_6: return 22; case CL_KEY_5: return 23; case CL_KEY_EQUALS: return 24; case CL_KEY_9: return 25; case CL_KEY_7: return 26; case CL_KEY_SUBTRACT: return 27; case CL_KEY_8: return 28; case CL_KEY_0: return 29; // case CL_KEY_UMLAU: return 30; // key next to key next to P ?? case CL_KEY_O: return 31; case CL_KEY_U: return 32; // case CL_KEY_AA: return 33; // key next to P ?? case CL_KEY_I: return 34; case CL_KEY_P: return 35; case CL_KEY_ENTER: return 36; case CL_KEY_L: return 37; case CL_KEY_J: return 38; case CL_KEY_QUOTE: return 39; case CL_KEY_K: return 40; case CL_KEY_SEMICOLON: return 41; case CL_KEY_COMMA: return 43; case CL_KEY_DIVIDE: return 44; case CL_KEY_N: return 45; case CL_KEY_M: return 46; case CL_KEY_PERIOD: return 47; case CL_KEY_TAB: return 48; case CL_KEY_SPACE: return 49; case CL_KEY_TILDE: return 50; case CL_KEY_BACKSPACE: return 51; case CL_KEY_ESCAPE: return 53; case CL_KEY_SHIFT: return 56; case CL_KEY_MENU: return 58; case CL_KEY_CONTROL: return 59; case CL_KEY_NUMPAD_DECIMAL: return 65; case CL_KEY_NUMPAD_MULTIPLY:return 67; case CL_KEY_NUMPAD_ADD: return 69; case CL_KEY_NUMPAD_DIVIDE: return 75; case CL_KEY_NUMPAD_ENTER: return 76; case CL_KEY_NUMPAD_SUBTRACT:return 78; case CL_KEY_NUMPAD_EQUALS: return 81; case CL_KEY_NUMPAD_0: return 82; case CL_KEY_NUMPAD_1: return 83; case CL_KEY_NUMPAD_2: return 84; case CL_KEY_NUMPAD_3: return 85; case CL_KEY_NUMPAD_4: return 86; case CL_KEY_NUMPAD_5: return 87; case CL_KEY_NUMPAD_6: return 88; case CL_KEY_NUMPAD_7: return 89; case CL_KEY_NUMPAD_8: return 91; case CL_KEY_NUMPAD_9: return 92; case CL_KEY_F5: return 96; case CL_KEY_F6: return 97; case CL_KEY_F7: return 98; case CL_KEY_F3: return 99; case CL_KEY_F8: return 100; case CL_KEY_F9: return 101; case CL_KEY_F10: return 109; case CL_KEY_F12: return 111; case CL_KEY_HOME: return 115; case CL_KEY_PAGE_UP: return 116; case CL_KEY_DELETE: return 117; case CL_KEY_F4: return 118; case CL_KEY_END: return 119; case CL_KEY_F2: return 120; case CL_KEY_PAGE_DOWN: return 121; case CL_KEY_F1: return 122; case CL_KEY_LEFT: return 123; case CL_KEY_RIGHT: return 124; case CL_KEY_DOWN: return 125; case CL_KEY_UP: return 126; default: return clkey - 0x10000; } return 0; } std::set CL_DisplayWindow_OpenGL::modifiercode_to_clkeys(int m) { std::set r; if (m & shiftKey) r.insert(CL_KEY_SHIFT); if (m & optionKey) r.insert(CL_KEY_OPTION); if (m & controlKey) r.insert(CL_KEY_CONTROL); if (m & rightShiftKey) r.insert(CL_KEY_RSHIFT); if (m & rightControlKey)r.insert(CL_KEY_RCONTROL); if (m & cmdKey) r.insert(CL_KEY_COMMAND); if (m & alphaLock) r.insert(CL_KEY_CAPSLOCK); //if (m & rightOptionKey) r.insert([right option key]); return r; } int CL_DisplayWindow_OpenGL::keycode_to_clkey(int keycode) { switch (keycode) { case 0: return CL_KEY_A; case 1: return CL_KEY_S; case 2: return CL_KEY_D; case 3: return CL_KEY_F; case 4: return CL_KEY_H; case 5: return CL_KEY_G; case 6: return CL_KEY_Z; case 7: return CL_KEY_X; case 8: return CL_KEY_C; case 9: return CL_KEY_V; case 11: return CL_KEY_B; case 12: return CL_KEY_Q; case 13: return CL_KEY_W; case 14: return CL_KEY_E; case 15: return CL_KEY_R; case 17: return CL_KEY_T; case 16: return CL_KEY_Y; case 18: return CL_KEY_1; case 19: return CL_KEY_2; case 20: return CL_KEY_3; case 21: return CL_KEY_4; case 22: return CL_KEY_6; case 23: return CL_KEY_5; case 24: return CL_KEY_EQUALS; case 25: return CL_KEY_9; case 26: return CL_KEY_7; case 27: return CL_KEY_SUBTRACT; case 28: return CL_KEY_8; case 29: return CL_KEY_0; // case 30: return CL_KEY_UMLAU; // key next to key next to P ?? case 31: return CL_KEY_O; case 32: return CL_KEY_U; // case 33: return CL_KEY_AA; // key next to P ?? case 34: return CL_KEY_I; case 35: return CL_KEY_P; case 36: return CL_KEY_ENTER; case 37: return CL_KEY_L; case 38: return CL_KEY_J; case 39: return CL_KEY_QUOTE; case 40: return CL_KEY_K; case 41: return CL_KEY_SEMICOLON; case 43: return CL_KEY_COMMA; case 44: return CL_KEY_DIVIDE; case 45: return CL_KEY_N; case 46: return CL_KEY_M; case 47: return CL_KEY_PERIOD; case 48: return CL_KEY_TAB; case 49: return CL_KEY_SPACE; case 50: return CL_KEY_TILDE; case 51: return CL_KEY_BACKSPACE; case 53: return CL_KEY_ESCAPE; case 65: return CL_KEY_NUMPAD_DECIMAL; case 67: return CL_KEY_NUMPAD_MULTIPLY; case 69: return CL_KEY_NUMPAD_ADD; case 75: return CL_KEY_NUMPAD_DIVIDE; case 76: return CL_KEY_NUMPAD_ENTER; case 78: return CL_KEY_NUMPAD_SUBTRACT; case 82: return CL_KEY_NUMPAD_0; case 83: return CL_KEY_NUMPAD_1; case 84: return CL_KEY_NUMPAD_2; case 85: return CL_KEY_NUMPAD_3; case 86: return CL_KEY_NUMPAD_4; case 87: return CL_KEY_NUMPAD_5; case 88: return CL_KEY_NUMPAD_6; case 89: return CL_KEY_NUMPAD_7; case 91: return CL_KEY_NUMPAD_8; case 92: return CL_KEY_NUMPAD_9; case 96: return CL_KEY_F5; case 97: return CL_KEY_F6; case 98: return CL_KEY_F7; case 99: return CL_KEY_F3; case 100: return CL_KEY_F8; case 101: return CL_KEY_F9; case 109: return CL_KEY_F10; case 111: return CL_KEY_F12; case 115: return CL_KEY_HOME; case 116: return CL_KEY_PAGE_UP; case 118: return CL_KEY_F4; case 119: return CL_KEY_END; case 120: return CL_KEY_F2; case 121: return CL_KEY_PAGE_DOWN; case 122: return CL_KEY_F1; case 123: return CL_KEY_LEFT; case 124: return CL_KEY_RIGHT; case 125: return CL_KEY_DOWN; case 126: return CL_KEY_UP; default: return keycode + 0x10000; } } OSStatus CL_DisplayWindow_OpenGL::on_window_event(EventHandlerCallRef call_ref, EventRef event_ref, void *user_data) { CL_DisplayWindow_OpenGL *self = (CL_DisplayWindow_OpenGL *) user_data; OSStatus result = CallNextEventHandler(call_ref, event_ref); UInt32 event_class = GetEventClass(event_ref); UInt32 event_kind = GetEventKind(event_ref); if (event_class == kEventClassKeyboard) { Point mouse_pos; UInt32 key_modifiers = 0; UInt32 click_count = 0; UInt32 key_code = 0; // Todo: // KeyboardLayoutRef layout; // KLGetCurrentKeyboardLayout(&layout); // KLGetKeyboardLayoutProperty(layout, kKLuchrData, &data); // call UCKeyTranslate to get unicode string for keys being pressed. GetMouse(&mouse_pos); GetEventParameter(event_ref, kEventParamKeyModifiers, typeUInt32, 0, sizeof(UInt32), 0, &key_modifiers); GetEventParameter(event_ref, kEventParamKeyCode, typeUInt32, 0, sizeof(UInt32), 0, &key_code); GetEventParameter(event_ref, kEventParamClickCount, typeUInt32, 0, sizeof(UInt32), 0, &click_count); if (!self->fullscreen) { Rect wbounds; GetWindowBounds(self->window_ref, kWindowContentRgn, &wbounds); mouse_pos.h -= wbounds.left; mouse_pos.v -= wbounds.top; } static unsigned long state = 0; static Ptr keymap = nil; Ptr new_keymap; // Get the current keyboard map resource new_keymap = (Ptr)GetScriptManagerVariable(smKCHRCache); if (new_keymap != keymap) { keymap = new_keymap; state = 0; } CL_InputEvent event; event.str = KeyTranslate(keymap, key_code|key_modifiers, &state) & 0xffff; event.device = self->keyboard; event.mouse_pos = CL_Point(mouse_pos.h, mouse_pos.v); event.repeat_count = click_count; self->mouse_pos = event.mouse_pos; // if kEventParamMouseLocation is not valid here, reverse this. switch (event_kind) { case kEventRawKeyModifiersChanged: { std::set current_keys = modifiercode_to_clkeys(key_modifiers); for (std::set::iterator i=current_keys.begin(); i!=current_keys.end(); i++) { if (!self->prev_modifier_keys.count(*i)) { event.id = *i; event.type = CL_InputEvent::pressed; self->keyboard.sig_key_down().call(event); } } for (std::set::iterator i=self->prev_modifier_keys.begin(); i!=self->prev_modifier_keys.end(); i++) { if (!current_keys.count(*i)) { event.id = *i; event.type = CL_InputEvent::released; self->keyboard.sig_key_up().call(event); } } self->prev_modifier_keys = current_keys; return noErr; } case kEventRawKeyDown: // first tests for cmd-q(12) or cmd-w(13) to close the window if needed if ((key_modifiers & cmdKey) && (key_code==12 || key_code==13)) { EventRef close_event; CreateEvent(NULL, kEventClassWindow, kEventWindowClose, 0, kEventAttributeNone, &close_event); EventTargetRef target = GetWindowEventTarget(self->window_ref); SendEventToEventTarget(close_event, target); return noErr; } else { event.id = keycode_to_clkey(key_code); event.type = CL_InputEvent::pressed; self->keyboard.sig_key_down().call(event); return noErr; } case kEventRawKeyRepeat: event.id = keycode_to_clkey(key_code); event.type = CL_InputEvent::pressed; self->keyboard.sig_key_down().call(event); return noErr; case kEventRawKeyUp: event.id = keycode_to_clkey(key_code); event.type = CL_InputEvent::released; self->keyboard.sig_key_up().call(event); return noErr; } } else if (event_class == kEventClassMouse) { Point mouse_pos; EventMouseButton mouse_button; UInt32 key_modifiers = 0; GetEventParameter(event_ref, kEventParamMouseLocation, typeQDPoint, 0, sizeof(Point), 0, &mouse_pos); GetEventParameter(event_ref, kEventParamMouseButton, typeMouseButton, 0, sizeof(EventMouseButton), 0, &mouse_button); GetEventParameter(event_ref, kEventParamKeyModifiers, typeUInt32, 0, sizeof(UInt32), 0, &key_modifiers); if (!self->fullscreen) { Rect wbounds; GetWindowBounds(self->window_ref, kWindowContentRgn, &wbounds); mouse_pos.h -= wbounds.left; mouse_pos.v -= wbounds.top; } CL_InputEvent event; event.id = mouse_button-1; event.device = self->mouse; event.mouse_pos = CL_Point(mouse_pos.h, mouse_pos.v); // Simulate second mouse button. if ((key_modifiers & controlKey) && (event.id == CL_MOUSE_LEFT)) event.id = CL_MOUSE_RIGHT; self->mouse_pos = event.mouse_pos; switch (event_kind) { case kEventMouseDown: self->mouse_states[event.id] = true; event.type = CL_InputEvent::pressed; self->mouse.sig_key_down().call(event); return noErr; case kEventMouseUp: self->mouse_states[event.id] = false; event.type = CL_InputEvent::released; self->mouse.sig_key_up().call(event); return noErr; case kEventMouseMoved: case kEventMouseDragged: self->mouse.sig_pointer_move().call(event); return noErr; case kEventMouseWheelMoved: //Note, this is not properly handling large quick wheel movements, but the implementation on other //platforms don't seem to either so I will do it their way for consistancy - mrfun EventMouseWheelAxis axis; GetEventParameter(event_ref, kEventParamMouseWheelAxis, typeMouseWheelAxis, NULL, sizeof(axis), NULL, &axis); if (axis == kEventMouseWheelAxisY) { long wheel_delta = 0; GetEventParameter(event_ref, kEventParamMouseWheelDelta, typeLongInteger, 0, sizeof(long), 0, &wheel_delta); //Ok, let me explain this bIgnoreThisOne thing. We are being passed two identical wheel events for every wheel movement - //This behavior is also in the apple example app GLCarbonAGLWindow and I can't figure out a way to get it to send only one //event or a difference in the events. (like a press and release tag differentiating them) //So the hack solution is to simply ignore every other mouse wheel event. This way, programs behave the same on every //platform instead of wheel zooming being twice as fast on macs. //If anybody has a better solution or finds a problem here please feel free to dig in. -mrfun static bool bIgnoreThisOne = true; bIgnoreThisOne = !bIgnoreThisOne; if (bIgnoreThisOne) if (wheel_delta) { if (wheel_delta > 0) { event.id = CL_MOUSE_WHEEL_UP; } else { event.id = CL_MOUSE_WHEEL_DOWN; } //sending both the down and up event, like the linux version event.type = CL_InputEvent::pressed; self->mouse.sig_key_down().call(event); event.type = CL_InputEvent::released; self->mouse.sig_key_up().call(event); } else { //handle the X axis someday? } } return noErr; } } else if (event_class == kEventClassWindow) { switch (event_kind) { case kEventWindowCollapsing: break; case kEventWindowDrawContent: { if (!self->fullscreen) { Rect rectPort; GetWindowPortBounds(self->window_ref, &rectPort); self->sig_paint( CL_Rect( rectPort.left, rectPort.top, rectPort.right, rectPort.bottom)); } } break; case kEventWindowShown: // called on initial show (not on un-minimize) break; case kEventWindowClose: self->sig_window_close(); return noErr; case kEventWindowActivated: case kEventWindowFocusAcquired: self->focus = true; self->sig_got_focus(); return noErr; case kEventWindowDeactivated: case kEventWindowFocusRelinquish: self->focus = false; self->sig_lost_focus(); return noErr; case kEventWindowBoundsChanged: { if (!self->fullscreen) { Rect rectPort; GetWindowPortBounds (self->window_ref, &rectPort); self->set_size(rectPort.right-rectPort.left, rectPort.bottom-rectPort.top); } } return noErr; case kEventWindowZoomed: // when maximized, but kEventWindowBoundsChanged is also called break; } } return result; }