mirror of
https://github.com/n64dev/cen64.git
synced 2024-06-21 13:32:40 -04:00
Commit the new X11 user interface.
Streamline the design/layout of the UI code. An excellent, unexpected side-effect of this commit resulted in a significant increase in performance...
This commit is contained in:
parent
bf38ee1879
commit
d348c23e2f
|
@ -261,6 +261,7 @@ if (DEFINED UNIX)
|
|||
find_package(X11 REQUIRED)
|
||||
include_directories(${X11_xf86vmode_INCLUDE_PATH})
|
||||
include_directories(${PROJECT_SOURCE_DIR}/os/x11)
|
||||
include_directories(${PROJECT_SOURCE_DIR}/os/posix)
|
||||
set(EXTRA_OS_LIBS ${X11_X11_LIB} ${X11_Xxf86vm_LIB})
|
||||
set(EXTRA_OS_EXE "")
|
||||
|
||||
|
@ -270,6 +271,7 @@ if (DEFINED UNIX)
|
|||
${PROJECT_SOURCE_DIR}/os/x11/gl_config.c
|
||||
${PROJECT_SOURCE_DIR}/os/x11/gl_window.c
|
||||
${PROJECT_SOURCE_DIR}/os/posix/alloc.c
|
||||
${PROJECT_SOURCE_DIR}/os/posix/timer.c
|
||||
${PROJECT_SOURCE_DIR}/os/posix/rom_file.c
|
||||
)
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
#include "os/windows/keycodes.h"
|
||||
|
||||
#else
|
||||
#include "os/unix/x11/keycodes.h"
|
||||
#include "os/x11/keycodes.h"
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
68
os/posix/thread.h
Normal file
68
os/posix/thread.h
Normal file
|
@ -0,0 +1,68 @@
|
|||
//
|
||||
// os/posix/thread.h: Multi-threading functions.
|
||||
//
|
||||
// CEN64: Cycle-Accurate Nintendo 64 Simulator.
|
||||
// Copyright (C) 2014, Tyler J. Stachecki.
|
||||
//
|
||||
// This file is subject to the terms and conditions defined in
|
||||
// 'LICENSE', which is part of this source code package.
|
||||
//
|
||||
|
||||
#ifndef CEN64_OS_POSIX_THREAD
|
||||
#define CEN64_OS_POSIX_THREAD
|
||||
#include "common.h"
|
||||
#include <pthread.h>
|
||||
|
||||
#define CEN64_THREAD_RETURN_TYPE void*
|
||||
#define CEN64_THREAD_RETURN_VAL NULL
|
||||
|
||||
// Type definitions.
|
||||
typedef pthread_t cen64_thread;
|
||||
typedef void* (*cen64_thread_func)(void *arg);
|
||||
|
||||
typedef pthread_mutex_t cen64_mutex;
|
||||
|
||||
//
|
||||
// Threads.
|
||||
//
|
||||
|
||||
// Creates a thread and starts executing 'f' within the thread.
|
||||
static inline int cen64_thread_create(cen64_thread *t,
|
||||
cen64_thread_func f, void *arg) {
|
||||
return pthread_create(t, NULL, f, arg);
|
||||
}
|
||||
|
||||
//
|
||||
// Join a thread created with cen64_thread_create. Use this to
|
||||
// effectively "free" the resources acquired for the thread.
|
||||
//
|
||||
static inline int cen64_thread_join(cen64_thread *t) {
|
||||
return pthread_join(*t, NULL);
|
||||
}
|
||||
|
||||
//
|
||||
// Mutexes.
|
||||
//
|
||||
|
||||
// Allocates resources for/initializes a mutex.
|
||||
static inline int cen64_mutex_create(cen64_mutex *m) {
|
||||
return pthread_mutex_init(m, NULL);
|
||||
}
|
||||
|
||||
// Releases resources acquired by cen64_mutex_create.
|
||||
static inline int cen64_mutex_destroy(cen64_mutex *m) {
|
||||
return pthread_mutex_destroy(m);
|
||||
}
|
||||
|
||||
// Locks the mutex passed as an argument.
|
||||
static inline int cen64_mutex_lock(cen64_mutex *m) {
|
||||
return pthread_mutex_lock(m);
|
||||
}
|
||||
|
||||
// Unlocks the mutex passed as an argument.
|
||||
static inline int cen64_mutex_unlock(cen64_mutex *m) {
|
||||
return pthread_mutex_unlock(m);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
36
os/posix/timer.c
Normal file
36
os/posix/timer.c
Normal file
|
@ -0,0 +1,36 @@
|
|||
//
|
||||
// os/posix/timer.c: Functions for obtaining the system clock time.
|
||||
//
|
||||
// CEN64: Cycle-Accurate Nintendo 64 Simulator.
|
||||
// Copyright (C) 2014, 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 "os/posix/timer.h"
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
// Computes the difference, in ns, between two times.
|
||||
unsigned long long compute_time_difference(
|
||||
const cen64_time *now, const cen64_time *before) {
|
||||
#if defined(__APPLE__)
|
||||
return (now->tv_sec - before->tv_sec) * NS_PER_SEC +
|
||||
(now->tv_usec - before->tv_usec) * NS_PER_USEC;
|
||||
#else
|
||||
return (now->tv_sec - before->tv_sec) * NS_PER_SEC +
|
||||
(now->tv_nsec - before->tv_nsec);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Gets the time from the most monotonic source possible.
|
||||
void get_time(cen64_time *t) {
|
||||
#if defined(__APPLE__)
|
||||
gettimeofday(t, NULL);
|
||||
#else
|
||||
clock_gettime(GETTIME_SOURCE, t);
|
||||
#endif
|
||||
}
|
||||
|
38
os/posix/timer.h
Normal file
38
os/posix/timer.h
Normal file
|
@ -0,0 +1,38 @@
|
|||
//
|
||||
// os/posix/timer.h: Functions for obtaining the system clock time.
|
||||
//
|
||||
// CEN64: Cycle-Accurate Nintendo 64 Simulator.
|
||||
// Copyright (C) 2014, Tyler J. Stachecki.
|
||||
//
|
||||
// This file is subject to the terms and conditions defined in
|
||||
// 'LICENSE', which is part of this source code package.
|
||||
//
|
||||
|
||||
#ifndef CEN64_OS_POSIX_TIMER
|
||||
#define CEN64_OS_POSIX_TIMER
|
||||
#include "common.h"
|
||||
|
||||
#define NS_PER_SEC 1000000000ULL
|
||||
|
||||
#if defined(CLOCK_MONOTONIC_PRECISE)
|
||||
#define GETTIME_SOURCE CLOCK_MONOTONIC_PRECISE
|
||||
#else
|
||||
#define GETTIME_SOURCE CLOCK_MONOTONIC_RAW
|
||||
#endif
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include <time.h>
|
||||
typedef struct timeval cen64_time;
|
||||
|
||||
#else
|
||||
#include <time.h>
|
||||
typedef struct timespec cen64_time;
|
||||
#endif
|
||||
|
||||
cen64_cold unsigned long long compute_time_difference(
|
||||
const cen64_time *now, const cen64_time *before);
|
||||
|
||||
cen64_cold void get_time(cen64_time *t);
|
||||
|
||||
#endif
|
||||
|
|
@ -14,7 +14,6 @@
|
|||
#include "os/common/alloc.h"
|
||||
#include "os/gl_window.h"
|
||||
#include "os/main.h"
|
||||
#include "os/unix/x11/glx_window.h"
|
||||
#include <fcntl.h>
|
||||
#include <signal.h>
|
||||
#include <stddef.h>
|
||||
|
@ -36,34 +35,10 @@ int main(int argc, const char *argv[]) {
|
|||
return cen64_main(argc, argv);
|
||||
}
|
||||
|
||||
// Grabs the input lock.
|
||||
void os_acquire_input(struct gl_window *gl_window) {
|
||||
struct glx_window *glx_window = (struct glx_window *) (gl_window->window);
|
||||
|
||||
pthread_mutex_lock(&glx_window->event_lock);
|
||||
}
|
||||
|
||||
// Releases the input lock.
|
||||
void os_release_input(struct gl_window *gl_window) {
|
||||
struct glx_window *glx_window = (struct glx_window *) (gl_window->window);
|
||||
|
||||
pthread_mutex_unlock(&glx_window->event_lock);
|
||||
}
|
||||
|
||||
// Informs the simulation thread if an exit was requested.
|
||||
#if 0
|
||||
bool os_exit_requested(struct gl_window *gl_window) {
|
||||
struct glx_window *glx_window = (struct glx_window *) (gl_window->window);
|
||||
|
||||
return glx_window_exit_requested(glx_window);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Allocates memory for a new device, runs it.
|
||||
int os_main(struct cen64_device *device, struct cen64_options *options) {
|
||||
struct gl_window_hints hints;
|
||||
struct glx_window window;
|
||||
pthread_t device_thread;
|
||||
cen64_thread thread;
|
||||
|
||||
#if 0
|
||||
// Spawn the user interface (or signal handler).
|
||||
|
@ -96,7 +71,15 @@ int os_main(struct cen64_device *device, struct cen64_options *options) {
|
|||
}
|
||||
}
|
||||
|
||||
device_run(device);
|
||||
if (cen64_thread_create(&thread, run_device_thread, device)) {
|
||||
printf("Failed to create the main emulation thread.\n");
|
||||
device_destroy(device);
|
||||
return 1;
|
||||
}
|
||||
|
||||
cen64_gl_window_thread(device);
|
||||
cen64_thread_join(&thread);
|
||||
// device_run(device);
|
||||
#if 0
|
||||
// Start the device thread, hand over control to the UI thread on success.
|
||||
if ((pthread_create(&device_thread, NULL, run_device_thread, device)) == 0) {
|
||||
|
|
|
@ -1,63 +0,0 @@
|
|||
//
|
||||
// os/unix/x11/glx_window.h
|
||||
//
|
||||
// Convenience functions for managing rendering windows.
|
||||
//
|
||||
// This file is subject to the terms and conditions defined in
|
||||
// 'LICENSE', which is part of this source code package.
|
||||
//
|
||||
|
||||
#ifndef __unix_x11_glx_window_h__
|
||||
#define __unix_x11_glx_window_h__
|
||||
#include "common.h"
|
||||
#include "os/gl_window.h"
|
||||
|
||||
#include <pthread.h>
|
||||
#include <GL/glx.h>
|
||||
#include <X11/Xatom.h>
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/Xutil.h>
|
||||
#include <X11/extensions/xf86vmode.h>
|
||||
|
||||
struct glx_window {
|
||||
Display *display;
|
||||
XVisualInfo *visual_info;
|
||||
XF86VidModeModeInfo old_mode;
|
||||
|
||||
Atom wm_delete_message;
|
||||
XSetWindowAttributes attr;
|
||||
Window window;
|
||||
int screen;
|
||||
|
||||
GLXContext context;
|
||||
|
||||
// Constant state used by threads.
|
||||
int select_pipefds[2];
|
||||
|
||||
uint8_t fixed_pad[CACHE_LINE_SIZE];
|
||||
|
||||
// Locks and whatnot for events.
|
||||
pthread_mutex_t event_lock;
|
||||
|
||||
char went_fullscreen;
|
||||
bool exit_requested;
|
||||
|
||||
uint8_t event_pad[CACHE_LINE_SIZE];
|
||||
|
||||
// Locks and whatnot for rendering.
|
||||
pthread_mutex_t render_lock;
|
||||
pthread_cond_t render_cv;
|
||||
|
||||
unsigned frame_xres, frame_yres, frame_xskip, frame_type;
|
||||
uint8_t frame_data[MAX_FRAME_DATA_SIZE];
|
||||
bool frame_pending;
|
||||
|
||||
uint8_t render_pad[CACHE_LINE_SIZE];
|
||||
};
|
||||
|
||||
cen64_cold bool glx_window_exit_requested(struct glx_window *window);
|
||||
cen64_cold void glx_window_render_frame(struct glx_window *window, const void *data,
|
||||
unsigned xres, unsigned yres, unsigned xskip, unsigned type);
|
||||
|
||||
#endif
|
||||
|
|
@ -1,83 +0,0 @@
|
|||
//
|
||||
// os/unix/x11/keycodes.h
|
||||
//
|
||||
// Definitions for keycodes.
|
||||
//
|
||||
// This file is subject to the terms and conditions defined in
|
||||
// 'LICENSE', which is part of this source code package.
|
||||
//
|
||||
|
||||
#ifndef __unix_x11_keycodes_h__
|
||||
#define __unix_x11_keycodes_h__
|
||||
#include <X11/keysym.h>
|
||||
|
||||
// Letter keys.
|
||||
#define CEN64_KEY_A XK_a
|
||||
#define CEN64_KEY_B XK_b
|
||||
#define CEN64_KEY_C XK_c
|
||||
#define CEN64_KEY_D XK_d
|
||||
#define CEN64_KEY_E XK_e
|
||||
#define CEN64_KEY_F XK_f
|
||||
#define CEN64_KEY_G XK_g
|
||||
#define CEN64_KEY_H XK_h
|
||||
#define CEN64_KEY_I XK_i
|
||||
#define CEN64_KEY_J XK_j
|
||||
#define CEN64_KEY_K XK_k
|
||||
#define CEN64_KEY_L XK_l
|
||||
#define CEN64_KEY_M XK_m
|
||||
#define CEN64_KEY_N XK_n
|
||||
#define CEN64_KEY_O XK_o
|
||||
#define CEN64_KEY_P XK_p
|
||||
#define CEN64_KEY_Q XK_q
|
||||
#define CEN64_KEY_R XK_r
|
||||
#define CEN64_KEY_S XK_s
|
||||
#define CEN64_KEY_T XK_t
|
||||
#define CEN64_KEY_U XK_u
|
||||
#define CEN64_KEY_V XK_v
|
||||
#define CEN64_KEY_W XK_w
|
||||
#define CEN64_KEY_X XK_x
|
||||
#define CEN64_KEY_Y XK_y
|
||||
#define CEN64_KEY_Z XK_z
|
||||
|
||||
// Number keys.
|
||||
#define CEN64_KEY_0 XK_0
|
||||
#define CEN64_KEY_1 XK_1
|
||||
#define CEN64_KEY_2 XK_2
|
||||
#define CEN64_KEY_3 XK_3
|
||||
#define CEN64_KEY_4 XK_4
|
||||
#define CEN64_KEY_5 XK_5
|
||||
#define CEN64_KEY_6 XK_6
|
||||
#define CEN64_KEY_7 XK_7
|
||||
#define CEN64_KEY_8 XK_8
|
||||
#define CEN64_KEY_9 XK_9
|
||||
|
||||
// Directional keys.
|
||||
#define CEN64_KEY_LEFT XK_Left
|
||||
#define CEN64_KEY_UP XK_Up
|
||||
#define CEN64_KEY_RIGHT XK_Right
|
||||
#define CEN64_KEY_DOWN XK_Down
|
||||
|
||||
// Other keys.
|
||||
#define CEN64_KEY_BSLASH XK_backslash
|
||||
#define CEN64_KEY_COMMA XK_comma
|
||||
#define CEN64_KEY_EQUALS XK_equal
|
||||
#define CEN64_KEY_FSLASH XK_fslash
|
||||
#define CEN64_KEY_MINUS XK_minus
|
||||
#define CEN64_KEY_LALT XK_Alt_L
|
||||
#define CEN64_KEY_LBRACKET XK_bracketleft
|
||||
#define CEN64_KEY_LCTRL XK_Control_L
|
||||
#define CEN64_KEY_LSHIFT XK_Shift_L
|
||||
#define CEN64_KEY_LSUPER XK_Super_L
|
||||
#define CEN64_KEY_PERIOD XK_period
|
||||
#define CEN64_KEY_RALT XK_Alt_R
|
||||
#define CEN64_KEY_RBRACKET XK_bracketright
|
||||
#define CEN64_KEY_RCTRL XK_Control_R
|
||||
#define CEN64_KEY_RETURN XK_Return
|
||||
#define CEN64_KEY_RSHIFT XK_Shift_R
|
||||
#define CEN64_KEY_RSUPER XK_Super_R
|
||||
#define CEN64_KEY_SEMICOLON XK_semicolon
|
||||
#define CEN64_KEY_SLASH XK_slash
|
||||
#define CEN64_KEY_SPACE XK_space
|
||||
|
||||
#endif
|
||||
|
|
@ -9,34 +9,91 @@
|
|||
//
|
||||
|
||||
#include "common.h"
|
||||
#include "device/device.h"
|
||||
#include "gl_common.h"
|
||||
#include "gl_config.h"
|
||||
#include "gl_display.h"
|
||||
#include "gl_screen.h"
|
||||
#include "gl_window.h"
|
||||
#include "timer.h"
|
||||
#include "vi/controller.h"
|
||||
#include "vi/render.h"
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <X11/Xlib.h>
|
||||
|
||||
static int cen64_gl_window_create_objects(cen64_gl_window window);
|
||||
|
||||
// Creates an (initially hidden) cen64_gl_window.
|
||||
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;
|
||||
|
||||
if ((window = malloc(sizeof(*window))) == NULL)
|
||||
return CEN64_GL_WINDOW_BAD;
|
||||
|
||||
// Get the visual info for the framebuffer configuration.
|
||||
if ((window->visual_info = glXGetVisualFromFBConfig(
|
||||
display, *config)) == NULL) {
|
||||
close(window->pipefds[0]);
|
||||
close(window->pipefds[1]);
|
||||
|
||||
cen64_mutex_destroy(&window->event_mutex);
|
||||
free(window);
|
||||
|
||||
return CEN64_GL_WINDOW_BAD;
|
||||
}
|
||||
|
||||
// Create synchronization primitives for the window.
|
||||
if (cen64_gl_window_create_objects(window)) {
|
||||
XFree(window->visual_info);
|
||||
free(window);
|
||||
|
||||
return CEN64_GL_WINDOW_BAD;
|
||||
}
|
||||
|
||||
// Create a colormap using the visual info.
|
||||
window->attr.colormap = XCreateColormap(display, XRootWindow(
|
||||
display, screen), window->visual_info->visual, AllocNone);
|
||||
|
||||
// Select the events we'd like to receive, create the window.
|
||||
window->attr.event_mask = ExposureMask | KeyPressMask | KeyReleaseMask |
|
||||
ButtonPressMask | StructureNotifyMask;
|
||||
|
||||
window->window = XCreateWindow(display, XRootWindow(display, screen),
|
||||
0, 0, 640, 480, 0, window->visual_info->depth, InputOutput,
|
||||
window->visual_info->visual, CWBorderPixel | CWColormap | CWEventMask,
|
||||
&window->attr);
|
||||
|
||||
// Now that we created the window, setup any atoms/properties we need.
|
||||
XSetStandardProperties(display, window->window, title,
|
||||
NULL, None, NULL, 0, NULL);
|
||||
|
||||
window->wm_delete_window = XInternAtom(display, "WM_DELETE_WINDOW", False);
|
||||
XSetWMProtocols(display, window->window, &window->wm_delete_window, 1);
|
||||
|
||||
window->exit_requested = false;
|
||||
window->display = display;
|
||||
window->screen = screen;
|
||||
return window;
|
||||
}
|
||||
|
||||
// Handles events that come from X11.
|
||||
bool cen64_gl_window_pump_events(struct vi_controller *vi) {
|
||||
bool released, exit_requested;
|
||||
bool released, exit_requested = false;
|
||||
XEvent event;
|
||||
|
||||
if (!XPending(vi->display))
|
||||
return false;
|
||||
|
||||
exit_requested = false;
|
||||
//pthread_mutex_lock(&glx_window->event_lock);
|
||||
cen64_mutex_lock(&vi->window->event_mutex);
|
||||
|
||||
do {
|
||||
XNextEvent(vi->display, &event);
|
||||
|
||||
switch (event.type) {
|
||||
case ClientMessage:
|
||||
//if ((unsigned) event.xclient.data.l[0] == glx_window->wm_delete_message)
|
||||
// glx_window->exit_requested = exit_requested = true;
|
||||
|
||||
vi->window->exit_requested = exit_requested = true;
|
||||
break;
|
||||
|
||||
case ConfigureNotify:
|
||||
|
@ -70,7 +127,101 @@ bool cen64_gl_window_pump_events(struct vi_controller *vi) {
|
|||
}
|
||||
} while (XPending(vi->display));
|
||||
|
||||
//pthread_mutex_unlock(&glx_window->event_lock);
|
||||
cen64_mutex_unlock(&vi->window->event_mutex);
|
||||
return exit_requested;
|
||||
}
|
||||
|
||||
// Allocate mutexes, pipes, etc. for the UI/window.
|
||||
int cen64_gl_window_create_objects(cen64_gl_window window) {
|
||||
if (cen64_mutex_create(&window->event_mutex)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (cen64_mutex_create(&window->render_mutex)) {
|
||||
cen64_mutex_destroy(&window->event_mutex);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (pipe(window->pipefds) < 0) {
|
||||
cen64_mutex_destroy(&window->render_mutex);
|
||||
cen64_mutex_destroy(&window->event_mutex);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Thread that controls the user interface, etc.
|
||||
int cen64_gl_window_thread(struct cen64_device *device) {
|
||||
struct vi_controller *vi = &device->vi;
|
||||
cen64_time last_update_time;
|
||||
cen64_gl_window window;
|
||||
int frame_count;
|
||||
|
||||
int max_fds, x11_fd;
|
||||
fd_set fdset;
|
||||
|
||||
// We listen for UI updates using a POSIX pipe, and X11
|
||||
// events/messages using the connection fd. select() is
|
||||
// then used to efficiently multiplex between the two,
|
||||
// so setup a fd_set, pipes, etc. required for all this.
|
||||
x11_fd = ConnectionNumber(vi->display);
|
||||
|
||||
max_fds = x11_fd > vi->window->pipefds[0] ? x11_fd: vi->window->pipefds[0];
|
||||
max_fds++;
|
||||
|
||||
FD_ZERO(&fdset);
|
||||
FD_SET(vi->window->pipefds[0], &fdset);
|
||||
FD_SET(x11_fd, &fdset);
|
||||
|
||||
// Okay, main UI thread loop starts here.
|
||||
for (frame_count = 0, get_time(&last_update_time) ; ;) {
|
||||
fd_set ready_to_read = fdset;
|
||||
|
||||
// Multiplex between all of our event sources...
|
||||
if (select(max_fds, &ready_to_read, NULL, NULL, NULL) > 0) {
|
||||
|
||||
// Did we get a X11 event?
|
||||
if (FD_ISSET(x11_fd, &ready_to_read)) {
|
||||
if (unlikely(cen64_gl_window_pump_events(vi)))
|
||||
break;
|
||||
}
|
||||
|
||||
// Did we get a UI event?
|
||||
if (FD_ISSET(vi->window->pipefds[0], &ready_to_read)) {
|
||||
read(vi->window->pipefds[0], &window, sizeof(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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -10,14 +10,16 @@
|
|||
|
||||
#ifndef CEN64_OS_X11_GL_WINDOW
|
||||
#define CEN64_OS_X11_GL_WINDOW
|
||||
#include "common.h"
|
||||
#include "gl_common.h"
|
||||
#include "gl_config.h"
|
||||
#include "gl_display.h"
|
||||
#include "gl_screen.h"
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include "thread.h"
|
||||
#include <unistd.h>
|
||||
#include <X11/Xlib.h>
|
||||
|
||||
#define FRAMEBUF_SZ (640 * 480 * 4)
|
||||
#define CEN64_GL_WINDOW_BAD (NULL)
|
||||
struct cen64_gl_window {
|
||||
cen64_gl_display display;
|
||||
|
@ -27,53 +29,27 @@ struct cen64_gl_window {
|
|||
Atom wm_delete_window;
|
||||
XSetWindowAttributes attr;
|
||||
XVisualInfo *visual_info;
|
||||
|
||||
int pipefds[2];
|
||||
|
||||
cen64_mutex render_mutex;
|
||||
uint8_t frame_buffer[FRAMEBUF_SZ];
|
||||
unsigned frame_hres, frame_vres;
|
||||
unsigned frame_hskip, frame_type;
|
||||
|
||||
cen64_mutex event_mutex;
|
||||
bool exit_requested;
|
||||
};
|
||||
|
||||
typedef struct cen64_gl_window *cen64_gl_window;
|
||||
|
||||
struct vi_controller;
|
||||
// Forward declaration.
|
||||
struct cen64_device;
|
||||
|
||||
// Creates a (hidden) cen64_gl_window.
|
||||
static inline cen64_gl_window cen64_gl_window_create(
|
||||
// Creates an (initially hidden) cen64_gl_window.
|
||||
cen64_cold 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;
|
||||
|
||||
if ((window = malloc(sizeof(*window))) == NULL)
|
||||
return CEN64_GL_WINDOW_BAD;
|
||||
|
||||
// Get the visual info for the framebuffer configuration.
|
||||
if ((window->visual_info = glXGetVisualFromFBConfig(
|
||||
display, *config)) == NULL) {
|
||||
free(window);
|
||||
|
||||
return CEN64_GL_WINDOW_BAD;
|
||||
}
|
||||
|
||||
// Create a colormap using the visual info.
|
||||
window->attr.colormap = XCreateColormap(display, XRootWindow(
|
||||
display, screen), window->visual_info->visual, AllocNone);
|
||||
|
||||
// Select the events we'd like to receive, create the window.
|
||||
window->attr.event_mask = ExposureMask | KeyPressMask | KeyReleaseMask |
|
||||
ButtonPressMask | StructureNotifyMask;
|
||||
|
||||
window->window = XCreateWindow(display, XRootWindow(display, screen),
|
||||
0, 0, 640, 480, 0, window->visual_info->depth, InputOutput,
|
||||
window->visual_info->visual, CWBorderPixel | CWColormap | CWEventMask,
|
||||
&window->attr);
|
||||
|
||||
// Now that we created the window, setup any atoms/properties we need.
|
||||
XSetStandardProperties(display, window->window, title,
|
||||
NULL, None, NULL, 0, NULL);
|
||||
|
||||
window->wm_delete_window = XInternAtom(display, "WM_DELETE_WINDOW", False);
|
||||
XSetWMProtocols(display, window->window, &window->wm_delete_window, 1);
|
||||
|
||||
window->display = display;
|
||||
window->screen = screen;
|
||||
return window;
|
||||
}
|
||||
const cen64_gl_config *config, const char *title);
|
||||
|
||||
// Releases resources allocated by cen64_gl_window_create.
|
||||
static inline void cen64_gl_window_destroy(cen64_gl_window window) {
|
||||
|
@ -81,17 +57,34 @@ static inline void cen64_gl_window_destroy(cen64_gl_window window) {
|
|||
XFreeColormap(window->display, window->attr.colormap);
|
||||
XFree(window->visual_info);
|
||||
|
||||
close(window->pipefds[0]);
|
||||
close(window->pipefds[1]);
|
||||
|
||||
cen64_mutex_destroy(&window->render_mutex);
|
||||
cen64_mutex_destroy(&window->event_mutex);
|
||||
free(window);
|
||||
}
|
||||
|
||||
// Handles events that come from X11.
|
||||
bool cen64_gl_window_pump_events(struct vi_controller *vi);
|
||||
// Pushes a notification to the UI queue to indicate a frame is ready.
|
||||
static inline void cen64_gl_window_push_frame(cen64_gl_window window) {
|
||||
write(window->pipefds[1], &window, sizeof(window));
|
||||
}
|
||||
|
||||
// Swaps the front and back buffers of the cen65_gl_window.
|
||||
// Sets the title of the cen64_gl_window.
|
||||
static inline void cen64_gl_window_set_title(
|
||||
cen64_gl_window window, const char *title) {
|
||||
XStoreName(window->display, window->window, title);
|
||||
XFlush(window->display);
|
||||
}
|
||||
|
||||
// Swaps the front and back buffers of the cen64_gl_window.
|
||||
static inline void cen64_gl_window_swap_buffers(cen64_gl_window window) {
|
||||
glXSwapBuffers(window->display, window->window);
|
||||
}
|
||||
|
||||
// Thread that controls the user interface, etc.
|
||||
int cen64_gl_window_thread(struct cen64_device *device);
|
||||
|
||||
// Unhides the cen64_gl_window.
|
||||
static inline void cen64_gl_window_unhide(cen64_gl_window window) {
|
||||
XMapRaised(window->display, window->window);
|
||||
|
|
|
@ -11,10 +11,11 @@
|
|||
#include "common.h"
|
||||
#include "bus/address.h"
|
||||
#include "bus/controller.h"
|
||||
#include "os/main.h"
|
||||
#include "gl_window.h"
|
||||
#include "ri/controller.h"
|
||||
#include "si/cic.h"
|
||||
#include "si/controller.h"
|
||||
#include "thread.h"
|
||||
#include "vi/controller.h"
|
||||
#include "vr4300/interface.h"
|
||||
#include <assert.h>
|
||||
|
@ -73,6 +74,7 @@ int pif_perform_command(struct si_controller *si,
|
|||
unsigned channel, uint8_t *send_buf, uint8_t send_bytes,
|
||||
uint8_t *recv_buf, uint8_t recv_bytes) {
|
||||
uint8_t command = send_buf[0];
|
||||
struct bus_controller *bus;
|
||||
|
||||
switch(command) {
|
||||
// Read status/reset.
|
||||
|
@ -107,9 +109,11 @@ int pif_perform_command(struct si_controller *si,
|
|||
case 0x01:
|
||||
switch(channel) {
|
||||
case 0:
|
||||
//os_acquire_input(&si->bus->vi->gl_window);
|
||||
memcpy(&bus, si, sizeof(bus));
|
||||
|
||||
cen64_mutex_lock(&bus->vi->window->event_mutex);
|
||||
memcpy(recv_buf, si->input, sizeof(si->input));
|
||||
//os_release_input(&si->bus->vi->gl_window);
|
||||
cen64_mutex_unlock(&bus->vi->window->event_mutex);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include "os/main.h"
|
||||
#include "ri/controller.h"
|
||||
#include "vi/controller.h"
|
||||
#include "vi/render.h"
|
||||
#include "vi/window.h"
|
||||
#include "vr4300/interface.h"
|
||||
|
||||
|
@ -53,19 +54,16 @@ int read_vi_regs(void *opaque, uint32_t address, uint32_t *word) {
|
|||
|
||||
// Advances the controller by one clock cycle.
|
||||
void vi_cycle(struct vi_controller *vi) {
|
||||
struct render_area *ra = &vi->render_area;
|
||||
int hskip, vres, hres;
|
||||
float hcoeff, vcoeff;
|
||||
unsigned type;
|
||||
cen64_gl_window window;
|
||||
|
||||
const uint8_t *buffer;
|
||||
uint32_t offset;
|
||||
struct render_area *ra = &vi->render_area;
|
||||
struct bus_controller *bus;
|
||||
float hcoeff, vcoeff;
|
||||
|
||||
if (likely(vi->counter-- != 0))
|
||||
return;
|
||||
|
||||
offset = vi->regs[VI_ORIGIN_REG] & 0xFFFFFF;
|
||||
buffer = vi->bus->ri->ram + offset;
|
||||
window = vi->window;
|
||||
|
||||
// Calculate the bounding positions.
|
||||
ra->x.start = vi->regs[VI_H_START_REG] >> 16 & 0x3FF;
|
||||
|
@ -76,30 +74,37 @@ void vi_cycle(struct vi_controller *vi) {
|
|||
hcoeff = (float) (vi->regs[VI_X_SCALE_REG] & 0xFFF) / (1 << 10);
|
||||
vcoeff = (float) (vi->regs[VI_Y_SCALE_REG] & 0xFFF) / (1 << 10);
|
||||
|
||||
// Calculate the height and width of the frame.
|
||||
vres = ra->height =((ra->y.end - ra->y.start) >> 1) * vcoeff;
|
||||
hres = ra->width = ((ra->x.end - ra->x.start)) * hcoeff;
|
||||
hskip = ra->hskip = vi->regs[VI_WIDTH_REG] - ra->width;
|
||||
type = vi->regs[VI_STATUS_REG] & 0x3;
|
||||
|
||||
if (hres <= 0 || vres <= 0)
|
||||
type = 0;
|
||||
|
||||
// Interact with the user interface?
|
||||
cen64_gl_window_pump_events(vi);
|
||||
gl_window_render_frame(vi, buffer, hres, vres, hskip, type);
|
||||
#if 0
|
||||
if (likely(vi->gl_window.window)) {
|
||||
if (os_exit_requested(&vi->gl_window))
|
||||
if (likely(window)) {
|
||||
cen64_mutex_lock(&window->event_mutex);
|
||||
|
||||
if (unlikely(window->exit_requested)) {
|
||||
cen64_mutex_unlock(&window->event_mutex);
|
||||
device_exit(vi->bus);
|
||||
}
|
||||
|
||||
os_render_frame(&vi->gl_window, buffer, hres, vres, hskip, type);
|
||||
cen64_mutex_unlock(&window->event_mutex);
|
||||
cen64_mutex_lock(&window->render_mutex);
|
||||
|
||||
// Calculate the height and width of the frame.
|
||||
window->frame_vres = ra->height =((ra->y.end - ra->y.start) >> 1) * vcoeff;
|
||||
window->frame_hres = ra->width = ((ra->x.end - ra->x.start)) * hcoeff;
|
||||
window->frame_hskip = ra->hskip = vi->regs[VI_WIDTH_REG] - ra->width;
|
||||
window->frame_type = vi->regs[VI_STATUS_REG] & 0x3;
|
||||
|
||||
if (window->frame_hres <= 0 || window->frame_vres <= 0)
|
||||
window->frame_type = 0;
|
||||
|
||||
// Copy the frame data into a temporary buffer.
|
||||
memcpy(&bus, vi, sizeof(bus));
|
||||
memcpy(vi->window->frame_buffer,
|
||||
bus->ri->ram + (vi->regs[VI_ORIGIN_REG] & 0xFFFFFF),
|
||||
sizeof(vi->window->frame_buffer));
|
||||
|
||||
cen64_mutex_unlock(&vi->window->render_mutex);
|
||||
cen64_gl_window_push_frame(window);
|
||||
}
|
||||
|
||||
else if (device_exit_requested)
|
||||
device_exit(vi->bus);
|
||||
#endif
|
||||
|
||||
// Raise an interrupt to indicate refresh.
|
||||
signal_rcp_interrupt(vi->bus->vr4300, MI_INTR_VI);
|
||||
vi->counter = VI_COUNTER_START;
|
||||
|
|
Loading…
Reference in a new issue