mirror of
https://github.com/n64dev/cen64.git
synced 2025-04-02 10:31:54 -04:00
205 lines
5.5 KiB
C
205 lines
5.5 KiB
C
//
|
|
// os/winapi/gl_window.h: WinAPI/OpenGL window definitions.
|
|
//
|
|
// CEN64: Cycle-Accurate Nintendo 64 Emulator.
|
|
// Copyright (C) 2015, Tyler J. Stachecki.
|
|
//
|
|
// This file is subject to the terms and conditions defined in
|
|
// 'LICENSE', which is part of this source code package.
|
|
//
|
|
|
|
#include "common.h"
|
|
#include "device/device.h"
|
|
#include "gl_common.h"
|
|
#include "gl_window.h"
|
|
#include "thread.h"
|
|
#include "timer.h"
|
|
#include "os/common/input.h"
|
|
#include "vi/controller.h"
|
|
#include "vi/render.h"
|
|
#include <windows.h>
|
|
|
|
static bool cen64_gl_window_pump_events(struct vi_controller *vi,
|
|
cen64_time *time, unsigned *frame_count);
|
|
|
|
static LRESULT CALLBACK WndProc(HWND hwnd,
|
|
UINT message, WPARAM w_param, LPARAM l_param) {
|
|
|
|
switch(message) {
|
|
case WM_DESTROY:
|
|
PostQuitMessage(0);
|
|
break;
|
|
|
|
case WM_SIZE:
|
|
gl_window_resize_cb(LOWORD(l_param), HIWORD(l_param));
|
|
break;
|
|
|
|
default:
|
|
return DefWindowProc(hwnd, message, w_param, l_param);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
// Handles events that come from Windows.
|
|
bool cen64_gl_window_pump_events(struct vi_controller *vi,
|
|
cen64_time *last_update_time, unsigned *frame_count) {
|
|
struct bus_controller *bus;
|
|
bool exit_requested = false;
|
|
MSG msg;
|
|
|
|
memcpy(&bus, vi, sizeof(bus));
|
|
|
|
while (1) {
|
|
GetMessage(&msg, NULL, 0, 0);
|
|
|
|
if (msg.message == WM_QUIT) {
|
|
cen64_mutex_lock(&vi->window->event_mutex);
|
|
vi->window->exit_requested = exit_requested = true;
|
|
cen64_mutex_unlock(&vi->window->event_mutex);
|
|
break;
|
|
}
|
|
|
|
else if (msg.message == WM_KEYDOWN) {
|
|
cen64_mutex_lock(&vi->window->event_mutex);
|
|
keyboard_press_callback(bus, msg.wParam);
|
|
cen64_mutex_unlock(&vi->window->event_mutex);
|
|
}
|
|
|
|
else if (msg.message == WM_KEYUP) {
|
|
cen64_mutex_lock(&vi->window->event_mutex);
|
|
keyboard_release_callback(bus, msg.wParam);
|
|
cen64_mutex_unlock(&vi->window->event_mutex);
|
|
}
|
|
|
|
else if (msg.message == WM_USER) {
|
|
cen64_gl_window window = vi->window;
|
|
|
|
cen64_mutex_lock(&window->render_mutex);
|
|
|
|
gl_window_render_frame(vi, window->frame_buffer,
|
|
window->frame_hres, window->frame_vres,
|
|
window->frame_hskip, window->frame_type);
|
|
|
|
cen64_mutex_unlock(&window->render_mutex);
|
|
|
|
// Update the window title every 60 VIs
|
|
// to display the current VI/s rate.
|
|
if (++(*frame_count) == 60) {
|
|
char title[128];
|
|
cen64_time current_time;
|
|
float ns;
|
|
|
|
// Compute time spent rendering last 60 frames, reset timer/counter.
|
|
get_time(¤t_time);
|
|
ns = compute_time_difference(¤t_time, last_update_time);
|
|
*last_update_time = current_time;
|
|
*frame_count = 0;
|
|
|
|
sprintf(title,
|
|
"CEN64 ["CEN64_COMPILER" - "CEN64_ARCH_DIR"/"CEN64_ARCH_SUPPORT"]"
|
|
" - %.1f VI/s", (60 / (ns / NS_PER_SEC)));
|
|
|
|
cen64_gl_window_set_title(window, title);
|
|
}
|
|
}
|
|
|
|
else {
|
|
TranslateMessage(&msg);
|
|
DispatchMessage(&msg);
|
|
}
|
|
}
|
|
|
|
return exit_requested;
|
|
}
|
|
|
|
cen64_gl_window cen64_gl_window_create(
|
|
cen64_gl_display display, cen64_gl_screen screen,
|
|
const cen64_gl_config *config, const char *title) {
|
|
cen64_gl_window window;
|
|
|
|
WNDCLASS wc;
|
|
DWORD dw_ex_style;
|
|
DWORD dw_style;
|
|
RECT window_rect;
|
|
|
|
if ((window = malloc(sizeof(*window))) == NULL)
|
|
return CEN64_GL_WINDOW_BAD;
|
|
|
|
window_rect.left = 0;
|
|
window_rect.right = 640;
|
|
window_rect.top = 0;
|
|
window_rect.bottom = 474;
|
|
|
|
window->hinstance = GetModuleHandle(NULL);
|
|
window->pixel_format = config->pixel_format;
|
|
|
|
memset(&wc, 0, sizeof(wc));
|
|
wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
|
|
wc.lpfnWndProc = (WNDPROC) WndProc;
|
|
wc.cbClsExtra = 0;
|
|
wc.cbWndExtra = 0;
|
|
wc.hInstance = window->hinstance;
|
|
wc.hIcon = LoadIcon(NULL, IDI_WINLOGO);
|
|
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
|
|
wc.hbrBackground = NULL;
|
|
wc.lpszMenuName = NULL;
|
|
wc.lpszClassName = "CEN64";
|
|
|
|
if (!RegisterClass(&wc)) {
|
|
free(window);
|
|
|
|
return CEN64_GL_WINDOW_BAD;
|
|
}
|
|
|
|
dw_ex_style = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;
|
|
dw_style = WS_OVERLAPPEDWINDOW;
|
|
AdjustWindowRectEx(&window_rect, dw_style, FALSE, dw_ex_style);
|
|
|
|
if ((window->hwnd = CreateWindowEx(dw_ex_style, "CEN64", title,
|
|
dw_style | WS_CLIPSIBLINGS | WS_CLIPCHILDREN, 0, 0,
|
|
window_rect.right - window_rect.left, window_rect.bottom - window_rect.top,
|
|
NULL, NULL, window->hinstance, NULL)) == NULL) {
|
|
UnregisterClass("CEN64", window->hinstance);
|
|
free(window);
|
|
|
|
return CEN64_GL_WINDOW_BAD;
|
|
}
|
|
|
|
// Store the pointer to the gl_window in the HWND attribute.
|
|
// We'll need it later to perform callbacks on resize and such.
|
|
// Also grab the window's device context so we can create a GL RC.
|
|
SetLastError(0);
|
|
SetWindowLongPtr(window->hwnd, GWLP_USERDATA, (uintptr_t) window);
|
|
|
|
if (GetLastError() || (window->hdc = GetDC(window->hwnd)) == NULL) {
|
|
DestroyWindow(window->hwnd);
|
|
UnregisterClass("CEN64", window->hinstance);
|
|
free(window);
|
|
|
|
return CEN64_GL_WINDOW_BAD;
|
|
}
|
|
|
|
SetPixelFormat(window->hdc, window->pixel_format, &config->pfd);
|
|
|
|
window->exit_requested = false;
|
|
cen64_mutex_create(&window->event_mutex);
|
|
cen64_mutex_create(&window->render_mutex);
|
|
window->thread_id = GetCurrentThreadId();
|
|
return window;
|
|
}
|
|
|
|
// Thread that controls the user interface, etc.
|
|
int cen64_gl_window_thread(struct cen64_device *device) {
|
|
cen64_time last_update_time;
|
|
unsigned frame_count;
|
|
|
|
get_time(&last_update_time);
|
|
frame_count = 0;
|
|
|
|
while (!cen64_gl_window_pump_events(&device->vi,
|
|
&last_update_time, &frame_count));
|
|
|
|
return 0;
|
|
}
|
|
|