nuclear@25: /* SimplyGL window system module for Win32/WGL */ nuclear@25: /* link-with: -lopengl32 */ nuclear@25: nuclear@25: #include "config.h" nuclear@25: nuclear@25: #ifdef USE_WSYS_MODULE_W32 nuclear@25: nuclear@25: #include nuclear@25: #include nuclear@25: #include nuclear@25: nuclear@25: struct window { nuclear@25: int id; nuclear@25: HWND win; nuclear@25: HDC dc; nuclear@25: HGLRC ctx; nuclear@25: int width, height; nuclear@25: int redisp_pending; nuclear@25: nuclear@25: struct window *next; nuclear@25: }; nuclear@25: nuclear@25: static int init(void); nuclear@25: static void shutdown(void); nuclear@25: nuclear@25: /* video mode switching */ nuclear@25: static int set_vidmode(int xsz, int ysz); nuclear@25: static int get_vidmode(int *xsz, int *ysz); nuclear@25: nuclear@25: /* create/destroy windows */ nuclear@25: static int create_window(int xsz, int ysz, unsigned int flags); nuclear@25: static void close_window(int id); nuclear@25: nuclear@25: /* window management */ nuclear@25: static int set_active(int id); nuclear@25: static struct window *find_window(int id); nuclear@25: static int activate_window(struct window *win); nuclear@25: static int set_title(const char *str); nuclear@25: static void redisplay(void); nuclear@25: static void swap_buffers(void); nuclear@25: nuclear@25: static int get_modifiers(void); nuclear@25: nuclear@25: /* event handling and friends */ nuclear@25: static void set_bits(long *mask, long bits); nuclear@25: static void clear_bits(long *mask, long bits); nuclear@25: static void set_event(int idx, int enable); nuclear@25: static int process_events(void); nuclear@25: nuclear@25: static LRESULT CALLBACK WindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam); nuclear@25: static int mouse_button(unsigned int msg); nuclear@25: nuclear@25: nuclear@25: static struct wsys_module ws = { nuclear@25: "win32", 0, nuclear@25: init, nuclear@25: shutdown, nuclear@25: set_vidmode, nuclear@25: get_vidmode, nuclear@25: create_window, nuclear@25: close_window, nuclear@25: set_active, nuclear@25: set_title, nuclear@25: redisplay, nuclear@25: swap_buffers, nuclear@25: get_modifiers, nuclear@25: set_event, nuclear@25: process_events, nuclear@25: 0 nuclear@25: }; nuclear@25: nuclear@25: static struct window *winlist; nuclear@25: static struct window *active_win, *prev_active; nuclear@25: static int next_id = 1; nuclear@25: nuclear@25: /* this is the only exported function, everything else should be static */ nuclear@25: void sgl_register_w32(void) nuclear@25: { nuclear@25: sgl_register_module(&ws); nuclear@25: } nuclear@25: nuclear@25: nuclear@25: static int init(void) nuclear@25: { nuclear@25: winlist = 0; nuclear@25: active_win = prev_active = 0; nuclear@25: return 0; nuclear@25: } nuclear@25: nuclear@25: static void shutdown(void) nuclear@25: { nuclear@25: /* TODO */ nuclear@25: } nuclear@25: nuclear@25: static int set_vidmode(int xsz, int ysz) nuclear@25: { nuclear@25: DEVMODE dmode; nuclear@25: memset(&dmode, 0, sizeof dmode); nuclear@25: dmode.dmSize = sizeof dmode; nuclear@25: /*dmode.dmBitsPerPel = 32;*/ nuclear@25: dmode.dmPelsWidth = xsz; nuclear@25: dmode.dmPelsHeight = ysz; nuclear@25: dmode.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT; nuclear@25: nuclear@25: if(ChangeDisplaySettings(&dmode, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL) { nuclear@25: sgl_log("failed to set video mode: %dx%d\n", xsz, ysz); nuclear@25: return -1; nuclear@25: } nuclear@25: return 0; nuclear@25: } nuclear@25: nuclear@25: static int get_vidmode(int *xsz, int *ysz) nuclear@25: { nuclear@25: /* TODO */ nuclear@25: return 0; nuclear@25: } nuclear@25: nuclear@25: static int create_window(int xsz, int ysz, unsigned int flags) nuclear@25: { nuclear@25: WNDCLASS wclass; nuclear@25: int win_width, win_height, win_x, win_y; nuclear@25: unsigned int style; nuclear@25: HINSTANCE pid; nuclear@25: HWND win; nuclear@25: PIXELFORMATDESCRIPTOR fmt; nuclear@25: int pf_id; nuclear@25: struct window *wnode; nuclear@25: void (*func)(); nuclear@25: nuclear@25: if(!(wnode = malloc(sizeof *wnode))) { nuclear@25: return -1; nuclear@25: } nuclear@25: nuclear@25: pid = GetModuleHandle(0); nuclear@25: nuclear@25: memset(&wclass, 0, sizeof wclass); nuclear@25: wclass.style = CS_VREDRAW | CS_HREDRAW | CS_OWNDC; nuclear@25: wclass.lpfnWndProc = handle_events; nuclear@25: wclass.hInstance = pid; nuclear@25: wclass.lpszClassName = "simplygl"; nuclear@25: nuclear@25: if(!RegisterClass(&wclass)) { nuclear@25: sgl_log("failed to register window class\n"); nuclear@25: return -1; nuclear@25: } nuclear@25: nuclear@25: calc_win_size(xsz, ysz, &win_width, &win_height, &win_x, &win_y); nuclear@25: nuclear@25: style = WS_BORDER | WS_CAPTION | WS_MINIMIZEBOX | WS_POPUP | WS_SYSMENU | WS_VISIBLE; nuclear@25: nuclear@25: if(!(win = CreateWindow("simplygl", "OpenGL/Win32", style, win_x, win_y, win_width, win_height, 0, 0, pid, 0))) { nuclear@25: sgl_log("failed to create window\n"); nuclear@25: return -1; nuclear@25: } nuclear@25: nuclear@25: ShowWindow(win, SW_SHOW); nuclear@25: SetForegroundWindow(win); nuclear@25: SetActiveWindow(win); nuclear@25: nuclear@25: wnode->win = win; nuclear@25: wnode->dc = GetDC(win); nuclear@25: nuclear@25: memset(&fmt, 0, sizeof fmt); nuclear@25: fmt.nSize = sizeof fmt; nuclear@25: fmt.nVersion = 1; nuclear@25: fmt.cColorBits = PFD_TYPE_RGBA; nuclear@25: fmt.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL; nuclear@25: nuclear@25: if(flags & SGL_DOUBLE) { nuclear@25: fmt.dwFlags |= PFD_DOUBLEBUFFER; nuclear@25: } nuclear@25: if(flags & SGL_DEPTH) { nuclear@25: fmt.cDepthBits = 24; nuclear@25: } nuclear@25: if(flags & SGL_STENCIL) { nuclear@25: fmt.cStencilBits = 8; nuclear@25: } nuclear@25: /* TODO: more... */ nuclear@25: nuclear@25: if(!(pf_id = ChoosePixelFormat(wnode->dc, &fmt))) { nuclear@25: sgl_log("can't find requested pixel format\n"); nuclear@25: return -1; nuclear@25: } nuclear@25: if(!SetPixelFormat(wnode->dc, pf_id, &fmt)) { nuclear@25: sgl_log("failed to set pixel format\n"); nuclear@25: return -1; nuclear@25: } nuclear@25: if(!(wnode->ctx = wglCreateContext(wnode->dc))) { nuclear@25: sgl_log("failed to create OpenGL context\n"); nuclear@25: return -1; nuclear@25: } nuclear@25: nuclear@25: wnode->id = next_id++; nuclear@25: wnode->width = xsz; nuclear@25: wnode->height = ysz; nuclear@25: wnode->redisp_pending = 1; nuclear@25: wnode->next = winlist; nuclear@25: winlist = wnode; nuclear@25: nuclear@25: if(!active_win) { nuclear@25: set_active(wnode->id); nuclear@25: } else { nuclear@25: activate_window(wnode); nuclear@25: } nuclear@25: nuclear@25: if((func = sgl_get_callback(SGL_CREATE))) { nuclear@25: func(wnode->id); nuclear@25: } nuclear@25: if((func = sgl_get_callback(SGL_RESHAPE))) { nuclear@25: func(xsz, ysz); nuclear@25: } nuclear@25: activate_window(prev_active); nuclear@25: return wnode->id; nuclear@25: } nuclear@25: nuclear@25: static void close_window(int id) nuclear@25: { nuclear@25: struct window dummy, *win, *prev; nuclear@25: sgl_close_callback_t close_func; nuclear@25: nuclear@25: dummy.next = winlist; nuclear@25: nuclear@25: prev = &dummy; nuclear@25: win = prev->next; nuclear@25: nuclear@25: while(win) { nuclear@25: if(win->id == id) { nuclear@25: if((close_func = sgl_get_callback(SGL_CLOSE))) { nuclear@25: close_func(id); nuclear@25: } nuclear@25: wglDeleteContext(win->ctx); nuclear@25: ReleaseDC(win->win, win->dc); nuclear@25: DestroyWindow(win->win); nuclear@25: nuclear@25: prev->next = win->next; nuclear@25: free(win); nuclear@25: return; nuclear@25: } nuclear@25: prev = win; nuclear@25: win = win->next; nuclear@25: } nuclear@25: } nuclear@25: nuclear@25: static int set_active(int id) nuclear@25: { nuclear@25: struct window *win = find_window(id); nuclear@25: if(!win) { nuclear@25: sgl_log("no such window: %d\n", id); nuclear@25: return -1; nuclear@25: } nuclear@25: /* only the user calls this, so don't revert this selection */ nuclear@25: prev_active = win; nuclear@25: return activate_window(win); nuclear@25: } nuclear@25: nuclear@25: static struct window *find_window(int id) nuclear@25: { nuclear@25: struct window *win = winlist; nuclear@25: nuclear@25: while(win) { nuclear@25: if(win->win == id) { nuclear@25: return win; nuclear@25: } nuclear@25: win = win->next; nuclear@25: } nuclear@25: return 0; nuclear@25: } nuclear@25: nuclear@25: static int activate_window(struct window *win) nuclear@25: { nuclear@25: if(!wglMakeCurrent(win->dc, win->ctx)) { nuclear@25: sgl_log("failed to activate window %d\n", (int)win->id); nuclear@25: return -1; nuclear@25: } nuclear@25: active_win = win; nuclear@25: return 0; nuclear@25: } nuclear@25: nuclear@25: static int set_title(const char *str) nuclear@25: { nuclear@25: SetWindowText(active_win->win, str); nuclear@25: } nuclear@25: nuclear@25: static void redisplay(void) nuclear@25: { nuclear@25: InvalidateRect(active_win->win, 0, 0); nuclear@25: } nuclear@25: nuclear@25: static void swap_buffers(void) nuclear@25: { nuclear@25: SwapBuffers(active_win->dc); nuclear@25: } nuclear@25: nuclear@25: static int get_modifiers(void) nuclear@25: { nuclear@25: return 0; /* TODO */ nuclear@25: } nuclear@25: nuclear@25: static void set_event(int idx, int enable) nuclear@25: { nuclear@25: /* not needed */ nuclear@25: } nuclear@25: nuclear@25: static int process_events(void) nuclear@25: { nuclear@25: int ret; nuclear@25: MSG msg; nuclear@25: sgl_idle_callback_t idle; nuclear@25: sgl_display_callback_t disp; nuclear@25: nuclear@25: idle = sgl_get_callback(SGL_IDLE); nuclear@25: nuclear@25: prev_active = active_win; nuclear@25: nuclear@25: if(!idle) { nuclear@25: if(!GetMessage(&msg, 0, 0, 0)) { nuclear@25: ret = -1; nuclear@25: goto end; nuclear@25: } nuclear@25: } else { nuclear@25: if(!PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) { nuclear@25: ret = 0; nuclear@25: goto end; nuclear@25: } nuclear@25: } nuclear@25: nuclear@25: do { nuclear@25: if(msg.message == WM_QUIT) { nuclear@25: ret = -1; nuclear@25: break; nuclear@25: } nuclear@25: TranslateMessage(&msg); nuclear@25: DispatchMessage(&msg); nuclear@25: } while(PeekMessage(&msg, 0, 0, 0, PM_REMOVE)); nuclear@25: nuclear@25: end: nuclear@25: if(ret != -1 && idle) { nuclear@25: idle(); nuclear@25: } nuclear@25: activate_window(prev_active); nuclear@25: return ret; nuclear@25: } nuclear@25: nuclear@25: static LRESULT CALLBACK WindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) nuclear@25: { nuclear@25: static int state; nuclear@25: struct window *win; nuclear@25: void (*func)(); nuclear@25: nuclear@25: if((win = find_window_handle(hwnd))) { nuclear@25: activate_window(win); nuclear@25: } else { nuclear@25: return DefWindowProc(hwnd, msg, wparam, lparam); nuclear@25: } nuclear@25: nuclear@25: switch(msg) { nuclear@25: case WM_PAINT: nuclear@25: if((func = sgl_get_callback(SGL_DISPLAY))) { nuclear@25: func(); nuclear@25: } nuclear@25: ValidateRect(hwnd, 0); nuclear@25: break; nuclear@25: nuclear@25: case WM_KEYDOWN: nuclear@25: case WM_KEYUP: nuclear@25: if((func = sgl_get_callback(SGL_KEYBOARD))) { nuclear@25: func(MapVirtualKey(wparam, 2), msg == WM_KEYDOWN); nuclear@25: } nuclear@25: break; nuclear@25: nuclear@25: case WM_LBUTTONDOWN: nuclear@25: case WM_RBUTTONDOWN: nuclear@25: case WM_MBUTTONDOWN: nuclear@25: if(1) { nuclear@25: state++; nuclear@25: } else { nuclear@25: case WM_LBUTTONUP: nuclear@25: case WM_RBUTTONUP: nuclear@25: case WM_MBUTTONUP: nuclear@25: state--; nuclear@25: } nuclear@25: assert(state >= 0); nuclear@25: if((func = sgl_get_callback(SGL_MOUSE))) { nuclear@25: func(mouse_button(msg), state, LOWORD(lparam), HIWORD(lparam)); nuclear@25: } nuclear@25: break; nuclear@25: nuclear@25: case WM_MOUSEMOVE: nuclear@25: if(state) { nuclear@25: func = sgl_get_callback(SGL_MOTION); nuclear@25: } else { nuclear@25: func = sgl_get_callback(SGL_PASSIVE); nuclear@25: } nuclear@25: if(func) { nuclear@25: func(LOWORD(lparam), HIWORD(lparam)); nuclear@25: } nuclear@25: break; nuclear@25: nuclear@25: default: nuclear@25: return DefWindowProc(hwnd, msg, wparam, lparam); nuclear@25: } nuclear@25: return 0; nuclear@25: } nuclear@25: nuclear@25: static int mouse_button(unsigned int msg) nuclear@25: { nuclear@25: switch(msg) { nuclear@25: case WM_LBUTTONDOWN: nuclear@25: case WM_LBUTTONUP: nuclear@25: return SGL_LEFT_BUTTON; nuclear@25: nuclear@25: case WM_RBUTTONDOWN: nuclear@25: case WM_RBUTTONUP: nuclear@25: return SGL_RIGHT_BUTTON; nuclear@25: nuclear@25: case WM_MBUTTONDOWN: nuclear@25: case WM_MBUTTONUP: nuclear@25: return SGL_MIDDLE_BUTTON; nuclear@25: nuclear@25: default: nuclear@25: break; nuclear@25: } nuclear@25: return -1; nuclear@25: } nuclear@25: nuclear@25: #else nuclear@25: int sgl_wsys_w32_silence_the_fucking_empty_file_warnings; nuclear@25: #endif