From fe9e38995f5a0abc196e9600c38d95385d6bf84f Mon Sep 17 00:00:00 2001 From: Jonas Suhr Christensen Date: Sat, 18 Jan 2014 10:17:58 +0100 Subject: Added PuGl window drawing. Enable with './configure -with-pugl'. --- pugl/pugl/pugl_win.cpp | 376 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 376 insertions(+) create mode 100644 pugl/pugl/pugl_win.cpp (limited to 'pugl/pugl/pugl_win.cpp') diff --git a/pugl/pugl/pugl_win.cpp b/pugl/pugl/pugl_win.cpp new file mode 100644 index 0000000..5f85d12 --- /dev/null +++ b/pugl/pugl/pugl_win.cpp @@ -0,0 +1,376 @@ +/* + Copyright 2012 David Robillard + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +/** + @file pugl_win.cpp Windows/WGL Pugl Implementation. +*/ + +#include +#include +#include + +#include +#include + +#include "pugl_internal.h" + +#ifndef WM_MOUSEWHEEL +# define WM_MOUSEWHEEL 0x020A +#endif +#ifndef WM_MOUSEHWHEEL +# define WM_MOUSEHWHEEL 0x020E +#endif +#ifndef WHEEL_DELTA +# define WHEEL_DELTA 120 +#endif + +const int LOCAL_CLOSE_MSG = WM_USER + 50; + +struct PuglInternalsImpl { + HWND hwnd; + HDC hdc; + HGLRC hglrc; + WNDCLASS wc; +}; + +LRESULT CALLBACK +wndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam); + +PuglView* +puglCreate(PuglNativeWindow parent, + const char* title, + int width, + int height, + bool resizable, + bool visible) +{ + PuglView* view = (PuglView*)calloc(1, sizeof(PuglView)); + PuglInternals* impl = (PuglInternals*)calloc(1, sizeof(PuglInternals)); + if (!view || !impl) { + return NULL; + } + + view->impl = impl; + view->width = width; + view->height = height; + + // FIXME: This is nasty, and pugl should not have static anything. + // Should class be a parameter? Does this make sense on other platforms? + static int wc_count = 0; + char classNameBuf[256]; + _snprintf(classNameBuf, sizeof(classNameBuf), "%s_%d\n", title, wc_count++); + + impl->wc.style = CS_OWNDC; + impl->wc.lpfnWndProc = wndProc; + impl->wc.cbClsExtra = 0; + impl->wc.cbWndExtra = 0; + impl->wc.hInstance = 0; + impl->wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); + impl->wc.hCursor = LoadCursor(NULL, IDC_ARROW); + impl->wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); + impl->wc.lpszMenuName = NULL; + impl->wc.lpszClassName = classNameBuf; + RegisterClass(&impl->wc); + + int winFlags = WS_POPUPWINDOW | WS_CAPTION; + if (resizable) { + winFlags |= WS_SIZEBOX; + } + + // Adjust the overall window size to accomodate our requested client size + RECT wr = { 0, 0, width, height }; + AdjustWindowRectEx(&wr, winFlags, FALSE, WS_EX_TOPMOST); + + impl->hwnd = CreateWindowEx( + WS_EX_TOPMOST, + classNameBuf, title, + (visible ? WS_VISIBLE : 0) | (parent ? WS_CHILD : winFlags), + 0, 0, wr.right-wr.left, wr.bottom-wr.top, + (HWND)parent, NULL, NULL, NULL); + + if (!impl->hwnd) { + free(impl); + free(view); + return NULL; + } + + SetWindowLongPtr(impl->hwnd, GWL_USERDATA, (LONG)view); + + impl->hdc = GetDC(impl->hwnd); + + PIXELFORMATDESCRIPTOR pfd; + ZeroMemory(&pfd, sizeof(pfd)); + pfd.nSize = sizeof(pfd); + pfd.nVersion = 1; + pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER; + pfd.iPixelType = PFD_TYPE_RGBA; + pfd.cColorBits = 24; + pfd.cDepthBits = 16; + pfd.iLayerType = PFD_MAIN_PLANE; + + int format = ChoosePixelFormat(impl->hdc, &pfd); + SetPixelFormat(impl->hdc, format, &pfd); + + impl->hglrc = wglCreateContext(impl->hdc); + wglMakeCurrent(impl->hdc, impl->hglrc); + + view->width = width; + view->height = height; + + return view; +} + +void +puglDestroy(PuglView* view) +{ + wglMakeCurrent(NULL, NULL); + wglDeleteContext(view->impl->hglrc); + ReleaseDC(view->impl->hwnd, view->impl->hdc); + DestroyWindow(view->impl->hwnd); + UnregisterClass(view->impl->wc.lpszClassName, NULL); + free(view->impl); + free(view); +} + +static void +puglReshape(PuglView* view, int width, int height) +{ + wglMakeCurrent(view->impl->hdc, view->impl->hglrc); + + if (view->reshapeFunc) { + view->reshapeFunc(view, width, height); + } else { + puglDefaultReshape(view, width, height); + } + + view->width = width; + view->height = height; +} + +void +puglDisplay(PuglView* view) +{ + wglMakeCurrent(view->impl->hdc, view->impl->hglrc); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glLoadIdentity(); + + if (view->displayFunc) { + view->displayFunc(view); + } + + glFlush(); + SwapBuffers(view->impl->hdc); + view->redisplay = false; +} + +static PuglKey +keySymToSpecial(int sym) +{ + switch (sym) { + case VK_F1: return PUGL_KEY_F1; + case VK_F2: return PUGL_KEY_F2; + case VK_F3: return PUGL_KEY_F3; + case VK_F4: return PUGL_KEY_F4; + case VK_F5: return PUGL_KEY_F5; + case VK_F6: return PUGL_KEY_F6; + case VK_F7: return PUGL_KEY_F7; + case VK_F8: return PUGL_KEY_F8; + case VK_F9: return PUGL_KEY_F9; + case VK_F10: return PUGL_KEY_F10; + case VK_F11: return PUGL_KEY_F11; + case VK_F12: return PUGL_KEY_F12; + case VK_LEFT: return PUGL_KEY_LEFT; + case VK_UP: return PUGL_KEY_UP; + case VK_RIGHT: return PUGL_KEY_RIGHT; + case VK_DOWN: return PUGL_KEY_DOWN; + case VK_PRIOR: return PUGL_KEY_PAGE_UP; + case VK_NEXT: return PUGL_KEY_PAGE_DOWN; + case VK_HOME: return PUGL_KEY_HOME; + case VK_END: return PUGL_KEY_END; + case VK_INSERT: return PUGL_KEY_INSERT; + case VK_SHIFT: return PUGL_KEY_SHIFT; + case VK_CONTROL: return PUGL_KEY_CTRL; + case VK_MENU: return PUGL_KEY_ALT; + case VK_LWIN: return PUGL_KEY_SUPER; + case VK_RWIN: return PUGL_KEY_SUPER; + } + return (PuglKey)0; +} + +static void +processMouseEvent(PuglView* view, int button, bool press, LPARAM lParam) +{ + view->event_timestamp_ms = GetMessageTime(); + if (press) { + SetCapture(view->impl->hwnd); + } else { + ReleaseCapture(); + } + + if (view->mouseFunc) { + view->mouseFunc(view, button, press, + GET_X_LPARAM(lParam), + GET_Y_LPARAM(lParam)); + } +} + +static void +setModifiers(PuglView* view) +{ + view->mods = 0; + view->mods |= (GetKeyState(VK_SHIFT) < 0) ? PUGL_MOD_SHIFT : 0; + view->mods |= (GetKeyState(VK_CONTROL) < 0) ? PUGL_MOD_CTRL : 0; + view->mods |= (GetKeyState(VK_MENU) < 0) ? PUGL_MOD_ALT : 0; + view->mods |= (GetKeyState(VK_LWIN) < 0) ? PUGL_MOD_SUPER : 0; + view->mods |= (GetKeyState(VK_RWIN) < 0) ? PUGL_MOD_SUPER : 0; +} + +static LRESULT +handleMessage(PuglView* view, UINT message, WPARAM wParam, LPARAM lParam) +{ + PAINTSTRUCT ps; + PuglKey key; + + setModifiers(view); + switch (message) { + case WM_CREATE: + case WM_SHOWWINDOW: + case WM_SIZE: + RECT rect; + GetClientRect(view->impl->hwnd, &rect); + puglReshape(view, rect.right, rect.bottom); + view->width = rect.right; + view->height = rect.bottom; + break; + case WM_PAINT: + BeginPaint(view->impl->hwnd, &ps); + puglDisplay(view); + EndPaint(view->impl->hwnd, &ps); + break; + case WM_MOUSEMOVE: + if (view->motionFunc) { + view->motionFunc(view, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); + } + break; + case WM_LBUTTONDOWN: + processMouseEvent(view, 1, true, lParam); + break; + case WM_MBUTTONDOWN: + processMouseEvent(view, 2, true, lParam); + break; + case WM_RBUTTONDOWN: + processMouseEvent(view, 3, true, lParam); + break; + case WM_LBUTTONUP: + processMouseEvent(view, 1, false, lParam); + break; + case WM_MBUTTONUP: + processMouseEvent(view, 2, false, lParam); + break; + case WM_RBUTTONUP: + processMouseEvent(view, 3, false, lParam); + break; + case WM_MOUSEWHEEL: + if (view->scrollFunc) { + view->scrollFunc( + view, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), + (int16_t)HIWORD(wParam) / (float)WHEEL_DELTA); + } + break; + case WM_MOUSEHWHEEL: + if (view->scrollFunc) { + view->scrollFunc( + view, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), + (int16_t)HIWORD(wParam) / float(WHEEL_DELTA), 0); + } + break; + case WM_KEYDOWN: + view->event_timestamp_ms = (GetMessageTime()); + if (view->ignoreKeyRepeat && (lParam & (1 << 30))) { + break; + } // else nobreak + case WM_KEYUP: + if ((key = keySymToSpecial(wParam))) { + if (view->specialFunc) { + view->specialFunc(view, message == WM_KEYDOWN, key); + } + } else if (view->keyboardFunc) { + view->keyboardFunc(view, message == WM_KEYDOWN, wParam); + } + break; + case WM_QUIT: + case LOCAL_CLOSE_MSG: + if (view->closeFunc) { + view->closeFunc(view); + } + break; + default: + return DefWindowProc( + view->impl->hwnd, message, wParam, lParam); + } + + return 0; +} + +PuglStatus +puglProcessEvents(PuglView* view) +{ + MSG msg; + while (PeekMessage(&msg, view->impl->hwnd, 0, 0, PM_REMOVE)) { + handleMessage(view, msg.message, msg.wParam, msg.lParam); + } + + + if (view->redisplay) { + InvalidateRect(view->impl->hwnd, NULL, FALSE); + } + + return PUGL_SUCCESS; +} + +LRESULT CALLBACK +wndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + PuglView* view = (PuglView*)GetWindowLongPtr(hwnd, GWL_USERDATA); + switch (message) { + case WM_CREATE: + PostMessage(hwnd, WM_SHOWWINDOW, TRUE, 0); + return 0; + case WM_CLOSE: + PostMessage(hwnd, LOCAL_CLOSE_MSG, wParam, lParam); + return 0; + case WM_DESTROY: + return 0; + default: + if (view) { + return handleMessage(view, message, wParam, lParam); + } else { + return DefWindowProc(hwnd, message, wParam, lParam); + } + } +} + +void +puglPostRedisplay(PuglView* view) +{ + view->redisplay = true; +} + +PuglNativeWindow +puglGetNativeWindow(PuglView* view) +{ + return (PuglNativeWindow)view->impl->hwnd; +} -- cgit v1.2.3