mirror of
https://github.com/pruten/shoebill.git
synced 2024-12-27 14:30:32 +00:00
6773dd6020
- Fewer FPU-related crashes - Fewer video driver-related crashes - Cleaned up the SDL GUI - Bumped up cocoa GUI to v0.0.4
636 lines
19 KiB
C
636 lines
19 KiB
C
/*
|
|
* Copyright (c) 2014, Peter Rutenbar <pruten@gmail.com>
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright notice, this
|
|
* list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
* this list of conditions and the following disclaimer in the documentation
|
|
* and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
|
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <assert.h>
|
|
#include <string.h>
|
|
#include <pthread.h>
|
|
#include <unistd.h>
|
|
#include <errno.h>
|
|
#include <SDL2/SDL.h>
|
|
#include <SDL2/SDL_opengl.h>
|
|
#include "../core/shoebill.h"
|
|
|
|
static void _print_vers(void)
|
|
{
|
|
printf("Shoebill v0.0.4 - http://github.com/pruten/shoebill - Peter Rutenbar (c) 2014\n\n");
|
|
}
|
|
|
|
rb_tree *keymap;
|
|
static void _init_keyboard_map (void)
|
|
{
|
|
#define mapkeymod(u, a, m) do { \
|
|
assert((a >> 7) == 0); \
|
|
uint16_t value = ((m) << 8)| (a); \
|
|
rb_insert(keymap, u, &value, NULL); \
|
|
} while (0)
|
|
|
|
#define mapkey(_u, a) mapkeymod(_u, a, 0)
|
|
|
|
keymap = rb_new(p_new_pool(NULL), sizeof(uint16_t));
|
|
|
|
// Letters
|
|
mapkey('a', 0x00);
|
|
mapkey('b', 0x0b);
|
|
mapkey('c', 0x08);
|
|
mapkey('d', 0x02);
|
|
mapkey('e', 0x0e);
|
|
mapkey('f', 0x03);
|
|
mapkey('g', 0x05);
|
|
mapkey('h', 0x04);
|
|
mapkey('i', 0x22);
|
|
mapkey('j', 0x26);
|
|
mapkey('k', 0x28);
|
|
mapkey('l', 0x25);
|
|
mapkey('m', 0x2e);
|
|
mapkey('n', 0x2d);
|
|
mapkey('o', 0x1f);
|
|
mapkey('p', 0x23);
|
|
mapkey('q', 0x0c);
|
|
mapkey('r', 0x0f);
|
|
mapkey('s', 0x01);
|
|
mapkey('t', 0x11);
|
|
mapkey('u', 0x20);
|
|
mapkey('v', 0x09);
|
|
mapkey('w', 0x0d);
|
|
mapkey('x', 0x07);
|
|
mapkey('y', 0x10);
|
|
mapkey('z', 0x06);
|
|
|
|
// Numbers
|
|
mapkey('0', 0x1d);
|
|
mapkey('1', 0x12);
|
|
mapkey('2', 0x13);
|
|
mapkey('3', 0x14);
|
|
mapkey('4', 0x15);
|
|
mapkey('5', 0x17);
|
|
mapkey('6', 0x16);
|
|
mapkey('7', 0x1a);
|
|
mapkey('8', 0x1c);
|
|
mapkey('9', 0x19);
|
|
|
|
// Top row symbols
|
|
mapkeymod(')', 0x1d, modShift);
|
|
mapkeymod('!', 0x12, modShift);
|
|
mapkeymod('@', 0x13, modShift);
|
|
mapkeymod('#', 0x14, modShift);
|
|
mapkeymod('$', 0x15, modShift);
|
|
mapkeymod('%', 0x17, modShift);
|
|
mapkeymod('^', 0x16, modShift);
|
|
mapkeymod('&', 0x1a, modShift);
|
|
mapkeymod('*', 0x1c, modShift);
|
|
mapkeymod('(', 0x19, modShift);
|
|
|
|
// Other symbols (no shift)
|
|
mapkeymod('`', 0x32, 0);
|
|
mapkeymod('-', 0x1b, 0);
|
|
mapkeymod('=', 0x18, 0);
|
|
mapkeymod('[', 0x21, 0);
|
|
mapkeymod(']', 0x1e, 0);
|
|
mapkeymod('\\', 0x2a, 0);
|
|
mapkeymod(';', 0x29, 0);
|
|
mapkeymod('\'', 0x27, 0);
|
|
mapkeymod(',', 0x2b, 0);
|
|
mapkeymod('.', 0x2f, 0);
|
|
mapkeymod('/', 0x2c, 0);
|
|
|
|
// Other symbols (with shift)
|
|
mapkeymod('~', 0x32, modShift);
|
|
mapkeymod('_', 0x1b, modShift);
|
|
mapkeymod('+', 0x18, modShift);
|
|
mapkeymod('{', 0x21, modShift);
|
|
mapkeymod('}', 0x1e, modShift);
|
|
mapkeymod('|', 0x2a, modShift);
|
|
mapkeymod(':', 0x29, modShift);
|
|
mapkeymod('"', 0x27, modShift);
|
|
mapkeymod('<', 0x2b, modShift);
|
|
mapkeymod('>', 0x2f, modShift);
|
|
mapkeymod('?', 0x2c, modShift);
|
|
|
|
// Function keys
|
|
mapkey(SDLK_F1, 0x7a);
|
|
mapkey(SDLK_F2, 0x78);
|
|
mapkey(SDLK_F3, 0x63);
|
|
mapkey(SDLK_F4, 0x76);
|
|
mapkey(SDLK_F5, 0x60);
|
|
mapkey(SDLK_F6, 0x61);
|
|
mapkey(SDLK_F7, 0x62);
|
|
mapkey(SDLK_F8, 0x64);
|
|
mapkey(SDLK_F9, 0x65);
|
|
mapkey(SDLK_F10, 0x6d);
|
|
mapkey(SDLK_F11, 0x67);
|
|
mapkey(SDLK_F12, 0x6f);
|
|
mapkey(SDLK_F13, 0x69);
|
|
mapkey(SDLK_F14, 0x6b);
|
|
mapkey(SDLK_F15, 0x71);
|
|
|
|
// Arrows
|
|
mapkey(SDLK_UP, 0x3e);
|
|
mapkey(SDLK_DOWN, 0x3d);
|
|
mapkey(SDLK_RIGHT, 0x3c);
|
|
mapkey(SDLK_LEFT, 0x3b);
|
|
|
|
// Delete
|
|
mapkey(SDLK_DELETE, 0x75);
|
|
mapkey(SDLK_BACKSPACE, 0x33);
|
|
mapkey(SDLK_BACKSPACE, 0x33);
|
|
|
|
// Enter, NL, CR
|
|
mapkey(SDLK_RETURN2, 0x24);
|
|
mapkey(SDLK_RETURN, 0x24);
|
|
// mapkey(0x03, 0x24);
|
|
|
|
// Other keys
|
|
mapkey(SDLK_ESCAPE, 0x35); // escape
|
|
mapkey(SDLK_SPACE, 0x31); // space
|
|
mapkey(SDLK_TAB, 0x30); // tab
|
|
}
|
|
|
|
static void _display_frame (SDL_Window *win)
|
|
{
|
|
shoebill_video_frame_info_t frame = shoebill_get_video_frame(9, 0);
|
|
|
|
shoebill_send_vbl_interrupt(9);
|
|
|
|
glDrawBuffer(GL_BACK);
|
|
glClear(GL_COLOR_BUFFER_BIT);
|
|
|
|
glClearColor(0, 0, 0, 1.0);
|
|
|
|
glViewport(0, 0, frame.width, frame.height);
|
|
glRasterPos2i(0, frame.height);
|
|
glPixelStorei(GL_UNPACK_LSB_FIRST, GL_TRUE);
|
|
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
|
|
|
glPixelZoom(1.0, -1.0);
|
|
|
|
glDrawPixels(frame.width,
|
|
frame.height,
|
|
GL_RGBA,
|
|
GL_UNSIGNED_BYTE,
|
|
frame.buf);
|
|
|
|
SDL_GL_SwapWindow(win);
|
|
}
|
|
|
|
struct shoe_app_pram_data_t {
|
|
uint8_t pram[256];
|
|
FILE *f;
|
|
pthread_t threadid;
|
|
volatile _Bool updated, tear_down_thread;
|
|
};
|
|
|
|
struct {
|
|
const char *scsi_path[8];
|
|
const char *rom_path;
|
|
const char *relative_unix_path;
|
|
const char *pram_path;
|
|
|
|
uint32_t height, width;
|
|
uint32_t ram_megabytes;
|
|
_Bool verbose, use_tfb;
|
|
|
|
struct shoe_app_pram_data_t pram_data;
|
|
} user_params;
|
|
|
|
#define equals_arg(name) keylen = strlen(name); value = argv[i]+keylen; if (strncmp((name), argv[i], keylen) == 0)
|
|
|
|
#if !((defined WIN32) || (defined _WIN64))
|
|
#include <sys/types.h>
|
|
#include <pwd.h>
|
|
#include <uuid/uuid.h>
|
|
#endif
|
|
|
|
static char* _get_home_dir (const char *terminal_element)
|
|
{
|
|
char *result = NULL;
|
|
|
|
#if (defined WIN32) || (defined _WIN64)
|
|
if (getenv("USERPROFILE") != NULL) {
|
|
result = malloc(strlen(getenv("USERPROFILE")) + strlen(terminal_element) + 32);
|
|
sprintf(result, "%s\\%s", getenv("USERPROFILE"), terminal_element);
|
|
goto done;
|
|
}
|
|
|
|
if (getenv("HOMEDRIVE") && getenv("HOMEPATH")) {
|
|
result = malloc(strlen(getenv("HOMEDRIVE")) + strlen(getenv("HOMEPATH")) + strlen(terminal_element) + 32);
|
|
sprintf(result, "%s\\%s\\%s", getenv("HOMEDRIVE"), getenv("HOMEPATH"), terminal_element);
|
|
goto done;
|
|
}
|
|
|
|
#else
|
|
|
|
if (getenv("HOME") != NULL) {
|
|
result = malloc(strlen(getenv("HOME")) + strlen(terminal_element) + 32);
|
|
sprintf(result, "%s/%s", getenv("HOME"), terminal_element);
|
|
goto done;
|
|
}
|
|
|
|
struct passwd *pwd = getpwuid(getuid());
|
|
if (pwd) {
|
|
result = malloc(strlen(pwd->pw_dir) + strlen(terminal_element) + 32);
|
|
sprintf(result, "%s/%s", pwd->pw_dir, terminal_element);
|
|
goto done;
|
|
}
|
|
|
|
#endif
|
|
|
|
done:
|
|
|
|
// printf("_get_home_dir: debug: %s\n", result);
|
|
|
|
return result;
|
|
}
|
|
|
|
static void _print_help (void)
|
|
{
|
|
printf("Arguments have the form name=value.\n");
|
|
printf("\n");
|
|
printf("rom=<path to Mac II ROM>\n");
|
|
printf("Specifies the path to a Macintosh II ROM.\n");
|
|
printf("E.g. rom=/home/foo/macii.rom\n");
|
|
printf("\n");
|
|
printf("disk0..disk6=<path to disk image>\n");
|
|
printf("Specifies the path to a disk image for the given SCSI ID. Shoebill will always boot from disk0, so make sure disk0 points to a bootable A/UX image.\n");
|
|
printf("E.g. disk0=/home/foo/aux3.img disk1=/blah.img\n");
|
|
printf("\n");
|
|
printf("ram=<megabytes of memory>\n");
|
|
printf("E.g. ram=16\n");
|
|
printf("\n");
|
|
printf("height=<num pixels>\n");
|
|
printf("Specifies the height of the screen in pixels.\n");
|
|
printf("\n");
|
|
printf("width=<num pixels>\n");
|
|
printf("Specifies the width of the screen in pixels.\n");
|
|
printf("\n");
|
|
printf("pram-path=<path to PRAM file>\n");
|
|
printf("Defaults to ~/.shoebill_pram\n");
|
|
printf("\n");
|
|
printf("verbose=<1 or 0>\n");
|
|
printf("Whether to boot A/UX in verbose mode. Best to leave it at default (1).\n");
|
|
printf("\n");
|
|
printf("unix-path=<path to kernel on disk0>\n");
|
|
printf("Path to the kernel file on the root disk image. Best to leave it at default (/unix).\n");
|
|
printf("\n");
|
|
printf("\n");
|
|
printf("Examples:\n");
|
|
printf("\n");
|
|
printf("shoebill.exe disk0=C:\\aux3.img rom=C:\\macii.rom width=1024 height=768 ram=64\n");
|
|
printf("\n");
|
|
printf("./shoebill disk0=/aux3.img rom=/macii.rom width=1024 height=768 ram=64\n");
|
|
printf("\n");
|
|
}
|
|
|
|
static void _init_user_params (int argc, char **argv)
|
|
{
|
|
char *key;
|
|
uint32_t i;
|
|
for (i=0; i<8; i++)
|
|
user_params.scsi_path[i] = NULL;
|
|
|
|
user_params.rom_path = "macii.rom";
|
|
user_params.relative_unix_path = "/unix";
|
|
|
|
user_params.height = 640;
|
|
user_params.width = 800;
|
|
user_params.ram_megabytes = 16;
|
|
user_params.verbose = 1;
|
|
user_params.use_tfb = 0;
|
|
|
|
user_params.pram_path = _get_home_dir(".shoebill_pram");
|
|
|
|
if (argc < 2) {
|
|
_print_help();
|
|
exit(0);
|
|
}
|
|
|
|
for (i=1; i<argc; i++) {
|
|
key = "-h";
|
|
if (strncmp(key, argv[i], strlen(key)) == 0) {
|
|
_print_help();
|
|
exit(0);
|
|
}
|
|
|
|
key = "help";
|
|
if (strncmp(key, argv[i], strlen(key)) == 0) {
|
|
_print_help();
|
|
exit(0);
|
|
}
|
|
|
|
key = "toby"; // Whether to use the "toby frame buffer" card, instead of the regular shoebill video card
|
|
if(strncmp(key, argv[i], strlen(key)) == 0) {
|
|
user_params.use_tfb = 1;
|
|
continue;
|
|
}
|
|
|
|
key = "ram=";
|
|
if (strncmp(key, argv[i], strlen(key)) == 0) {
|
|
user_params.ram_megabytes = strtoul(argv[i]+strlen(key), NULL, 10);
|
|
continue;
|
|
}
|
|
|
|
key = "height=";
|
|
if (strncmp(key, argv[i], strlen(key)) == 0) {
|
|
user_params.height = strtoul(argv[i]+strlen(key), NULL, 10);
|
|
continue;
|
|
}
|
|
|
|
key = "width=";
|
|
if (strncmp(key, argv[i], strlen(key)) == 0) {
|
|
user_params.width = strtoul(argv[i]+strlen(key), NULL, 10);
|
|
continue;
|
|
}
|
|
|
|
key = "verbose=";
|
|
if (strncmp(key, argv[i], strlen(key)) == 0) {
|
|
user_params.verbose = strtoul(argv[i]+strlen(key), NULL, 10);
|
|
continue;
|
|
}
|
|
|
|
key = "rom=";
|
|
if (strncmp(key, argv[i], strlen(key)) == 0) {
|
|
user_params.rom_path = argv[i] + strlen(key);
|
|
continue;
|
|
}
|
|
|
|
key = "unix-path=";
|
|
if (strncmp(key, argv[i], strlen(key)) == 0) {
|
|
user_params.relative_unix_path = argv[i] + strlen(key);
|
|
continue;
|
|
}
|
|
|
|
key = "pram-path=";
|
|
if (strncmp(key, argv[i], strlen(key)) == 0) {
|
|
user_params.pram_path = argv[i] + strlen(key);
|
|
continue;
|
|
}
|
|
|
|
if ((strncmp("disk", argv[i], 4) == 0) && (isdigit(argv[i][4])) && (argv[i][5] == '=')) {
|
|
uint8_t scsi_num = argv[i][4] - '0';
|
|
if (scsi_num < 7) {
|
|
user_params.scsi_path[scsi_num] = &argv[i][6];
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
void _pram_callback (void *param, const uint8_t addr, const uint8_t byte)
|
|
{
|
|
struct shoe_app_pram_data_t *pram_data = (struct shoe_app_pram_data_t*)param;
|
|
pram_data->pram[addr] = byte;
|
|
pram_data->updated = 1;
|
|
}
|
|
|
|
|
|
void* _pram_writer_thread (void *param)
|
|
{
|
|
struct shoe_app_pram_data_t *pram_data = (struct shoe_app_pram_data_t*)param;
|
|
while (!pram_data->tear_down_thread) {
|
|
if (pram_data->updated) {
|
|
pram_data->updated = 0;
|
|
rewind(pram_data->f);
|
|
assert(fwrite(pram_data->pram, 256, 1, pram_data->f) == 1);
|
|
fflush(stdout);
|
|
pram_data->tear_down_thread = 0;
|
|
}
|
|
sleep(1);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
static _Bool _setup_shoebill (void)
|
|
{
|
|
uint32_t i;
|
|
shoebill_config_t config;
|
|
|
|
memset(&config, 0, sizeof(shoebill_config_t));
|
|
|
|
config.aux_verbose = user_params.verbose;
|
|
config.ram_size = user_params.ram_megabytes * 1024 * 1024;
|
|
config.aux_kernel_path = user_params.relative_unix_path;
|
|
config.rom_path = user_params.rom_path;
|
|
config.pram_callback = _pram_callback;
|
|
config.pram_callback_param = (void*)&user_params.pram_data;
|
|
memcpy(config.pram, user_params.pram_data.pram, 256);
|
|
|
|
for (i=0; i<7; i++)
|
|
config.scsi_devices[i].path = user_params.scsi_path[i];
|
|
|
|
if (!shoebill_initialize(&config)) {
|
|
printf("%s\n", config.error_msg);
|
|
return 0;
|
|
}
|
|
|
|
if (user_params.use_tfb) {
|
|
shoebill_install_tfb_card(&config, 9);
|
|
}
|
|
else {
|
|
shoebill_install_video_card(&config,
|
|
9, // slotnum
|
|
user_params.width,
|
|
user_params.height);
|
|
}
|
|
|
|
|
|
shoebill_start();
|
|
return 1;
|
|
}
|
|
|
|
static void _handle_key_event (SDL_Event *event)
|
|
{
|
|
const SDL_Keycode sym = event->key.keysym.sym;
|
|
const _Bool key_down = (event->type == SDL_KEYDOWN);
|
|
const SDL_Keymod sdl_mod = SDL_GetModState();
|
|
uint16_t adb_mod = 0;
|
|
uint16_t value;
|
|
|
|
if (sdl_mod & KMOD_SHIFT) adb_mod |= modShift;
|
|
if (sdl_mod & KMOD_CTRL) adb_mod |= modControl;
|
|
if (sdl_mod & KMOD_ALT) adb_mod |= modOption;
|
|
if (sdl_mod & KMOD_GUI) adb_mod |= modCommand;
|
|
if (sdl_mod & KMOD_CAPS) adb_mod |= modCapsLock;
|
|
|
|
if (rb_find(keymap, sym, &value)) {
|
|
shoebill_key_modifier((value >> 8) | adb_mod);
|
|
shoebill_key(key_down, value & 0xff);
|
|
}
|
|
}
|
|
|
|
static _Bool _init_pram (void)
|
|
{
|
|
FILE *f = fopen(user_params.pram_path, "r+b");
|
|
memset(&user_params.pram_data, 0, sizeof(struct shoe_app_pram_data_t));
|
|
|
|
if ((f == NULL) || (fread(user_params.pram_data.pram, 256, 1, f) != 1)) {
|
|
if (f == NULL)
|
|
f = fopen(user_params.pram_path, "w+b");
|
|
if (f == NULL) {
|
|
printf("Can't open pram_path! [%s] [errno=%s]\n",
|
|
user_params.pram_path,
|
|
sys_errlist[errno]);
|
|
return 0;
|
|
}
|
|
rewind(f);
|
|
shoebill_validate_or_zap_pram(user_params.pram_data.pram, 1);
|
|
|
|
assert(fwrite(user_params.pram_data.pram, 256, 1, f) == 1);
|
|
fflush(f);
|
|
}
|
|
|
|
user_params.pram_data.f = f;
|
|
shoebill_validate_or_zap_pram(user_params.pram_data.pram, 0);
|
|
|
|
pthread_create(&user_params.pram_data.threadid,
|
|
NULL,
|
|
_pram_writer_thread,
|
|
&user_params.pram_data);
|
|
|
|
return 1;
|
|
}
|
|
|
|
int main (int argc, char **argv)
|
|
{
|
|
const uint32_t frame_ticks = 1000 / 60;
|
|
uint32_t last_frame_ticks;
|
|
_Bool capture_cursor;
|
|
|
|
_print_vers();
|
|
|
|
_init_keyboard_map();
|
|
_init_user_params(argc, argv);
|
|
if (!_init_pram())
|
|
return 0;
|
|
else if (!_setup_shoebill())
|
|
return 0;
|
|
|
|
|
|
|
|
shoebill_video_frame_info_t frame = shoebill_get_video_frame(9, 1);
|
|
|
|
SDL_Init(SDL_INIT_VIDEO);
|
|
|
|
SDL_Window *win = SDL_CreateWindow("Shoebill",
|
|
SDL_WINDOWPOS_UNDEFINED,
|
|
SDL_WINDOWPOS_UNDEFINED,
|
|
frame.width, frame.height,
|
|
SDL_WINDOW_OPENGL);
|
|
|
|
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
|
|
SDL_GLContext glctx = SDL_GL_CreateContext(win);
|
|
|
|
glShadeModel(GL_FLAT);
|
|
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
|
glClearColor(0.5, 0.5, 0.5, 1.0);
|
|
|
|
glMatrixMode(GL_MODELVIEW);
|
|
glLoadIdentity();
|
|
|
|
glMatrixMode(GL_PROJECTION);
|
|
glLoadIdentity();
|
|
glOrtho(0, frame.width, 0, frame.height, -1.0, 1.0);
|
|
|
|
capture_cursor = 1;
|
|
SDL_ShowCursor(0);
|
|
SDL_SetRelativeMouseMode(1);
|
|
|
|
SDL_GL_SetSwapInterval(1);
|
|
|
|
last_frame_ticks = SDL_GetTicks();
|
|
while (1) {
|
|
const uint32_t now = SDL_GetTicks();
|
|
uint32_t ticks_to_next_frame;
|
|
SDL_Event event;
|
|
|
|
if ((now - last_frame_ticks) >= frame_ticks) {
|
|
_display_frame(win);
|
|
last_frame_ticks = now;
|
|
ticks_to_next_frame = frame_ticks;
|
|
}
|
|
else
|
|
ticks_to_next_frame = frame_ticks - (now - last_frame_ticks);
|
|
|
|
event.type = SDL_USEREVENT;
|
|
SDL_WaitEventTimeout(&event, ticks_to_next_frame);
|
|
|
|
switch (event.type) {
|
|
case SDL_QUIT:
|
|
goto quit;
|
|
|
|
case SDL_MOUSEBUTTONDOWN: {
|
|
if ((event.button.button == SDL_BUTTON_LEFT) && capture_cursor)
|
|
shoebill_mouse_click(1);
|
|
break;
|
|
}
|
|
|
|
case SDL_MOUSEBUTTONUP: {
|
|
// If the cursor isn't captured, then any click will capture it
|
|
if (!capture_cursor)
|
|
capture_cursor = 1;
|
|
// If the cursor is captured, then left clicks get sent to the OS
|
|
else if (event.button.button == SDL_BUTTON_LEFT)
|
|
shoebill_mouse_click(0);
|
|
// If the cursor is captured, then right clicks will uncapture it
|
|
else if ((event.button.button == SDL_BUTTON_RIGHT) && capture_cursor)
|
|
capture_cursor = 0;
|
|
break;
|
|
}
|
|
|
|
case SDL_MOUSEMOTION: {
|
|
if (capture_cursor) {
|
|
_Bool down = event.motion.state & SDL_BUTTON(SDL_BUTTON_LEFT);
|
|
shoebill_mouse_click(down);
|
|
shoebill_mouse_move_delta(event.motion.xrel, event.motion.yrel);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case SDL_KEYDOWN:
|
|
case SDL_KEYUP:
|
|
if (!event.key.repeat)
|
|
_handle_key_event(&event);
|
|
break;
|
|
}
|
|
|
|
if (capture_cursor) {
|
|
SDL_ShowCursor(0);
|
|
SDL_SetRelativeMouseMode(1);
|
|
}
|
|
else {
|
|
SDL_ShowCursor(1);
|
|
SDL_SetRelativeMouseMode(0);
|
|
}
|
|
}
|
|
|
|
quit:
|
|
|
|
// FIXME: tear down the pram thread and flush pram
|
|
|
|
return 0;
|
|
}
|