shoebill/sdl-gui/sdl.c
Peter Rutenbar 1bee24316c Now more Windows-friendly
- cpu_thread now stops and waits on a pthread condition variable, rather
  than sleep(1)ing and waiting to be pthread_kill()'d. Signals don't
  work well on Windows, apparently.
- fopen() now open binary files with the "b" mode
- the keymap red-black tree no longer casts pointers to ints, because
  mingw/gcc complains about it
- added a dumb batch script to compile the sdl gui on windows
- {n,h}to{h,n}{s,l,ll} is now handled better on windows
2014-06-14 00:19:08 -04:00

423 lines
12 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 <SDL2/SDL.h>
#include <SDL2/SDL_opengl.h>
#include "../core/shoebill.h"
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 {
const char *scsi_path[8];
const char *rom_path;
const char *relative_unix_path;
uint32_t height, width;
uint32_t ram_megabytes;
_Bool verbose;
} user_params;
#define equals_arg(name) keylen = strlen(name); value = argv[i]+keylen; if (strncmp((name), argv[i], keylen) == 0)
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;
for (i=1; i<argc; i++) {
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;
}
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;
}
}
}
}
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;
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;
}
shoebill_install_video_card(&config,
9, // slotnum
user_params.width, // 1024,
user_params.height); // 768,
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);
}
}
int main (int argc, char **argv)
{
const uint32_t frame_ticks = 1000 / 60;
uint32_t last_frame_ticks;
_Bool capture_cursor;
_init_keyboard_map();
_init_user_params(argc, argv);
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);
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:
exit(0);
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);
}
}
return 0;
}