/* * simh sim_video support for XY display simulator * Mark Pizzolato * January 2016 * Based on win32.c module by Phil Budne */ /* * Copyright (c) 2016, Mark Pizzolato * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * * Except as contained in this notice, the names of the authors shall * not be used in advertising or otherwise to promote the sale, use or * other dealings in this Software without prior written authorization * from the authors. */ /* * BUGS: * Does not allow you to close display window; * would need to tear down both system, and system independent data. * */ #include "sim_video.h" #include #include #include "ws.h" #include "display.h" #ifndef PIX_SIZE #define PIX_SIZE 1 #endif /* * light pen location * see ws.h for full description */ int ws_lp_x = -1; int ws_lp_y = -1; /* A device simulator can optionally set the vid_display_kb_event_process * routine pointer to the address of a routine. * Simulator code which uses the display library which processes window * keyboard data with code in display/sim_ws.c can use this routine to * explicitly get access to keyboard events that arrive in the display * window. This routine should return 0 if it has handled the event that * was passed, and non zero if it didn't handle it. If the routine address * is not set or a non zero return value occurs, then the keyboard event * will be processed by the display library which may then be handled as * console character input if the device console code is implemented to * accept this. */ int (*vid_display_kb_event_process)(SIM_KEY_EVENT *kev) = NULL; static int xpixels, ypixels; static int pix_size = PIX_SIZE; static const char *window_name; static uint32 *colors = NULL; static uint32 ncolors = 0, size_colors = 0; static uint32 *surface = NULL; static uint32 ws_palette[2]; /* Monochrome palette */ typedef struct cursor { Uint8 *data; Uint8 *mask; int width; int height; int hot_x; int hot_y; } CURSOR; static CURSOR *arrow_cursor; static CURSOR *cross_cursor; static int map_key(int k) { switch (k) { case SIM_KEY_0: return '0'; case SIM_KEY_1: return '1'; case SIM_KEY_2: return '2'; case SIM_KEY_3: return '3'; case SIM_KEY_4: return '4'; case SIM_KEY_5: return '5'; case SIM_KEY_6: return '6'; case SIM_KEY_7: return '7'; case SIM_KEY_8: return '8'; case SIM_KEY_9: return '9'; case SIM_KEY_A: return 'a'; case SIM_KEY_B: return 'b'; case SIM_KEY_C: return 'c'; case SIM_KEY_D: return 'd'; case SIM_KEY_E: return 'e'; case SIM_KEY_F: return 'f'; case SIM_KEY_G: return 'g'; case SIM_KEY_H: return 'h'; case SIM_KEY_I: return 'i'; case SIM_KEY_J: return 'j'; case SIM_KEY_K: return 'k'; case SIM_KEY_L: return 'l'; case SIM_KEY_M: return 'm'; case SIM_KEY_N: return 'n'; case SIM_KEY_O: return 'o'; case SIM_KEY_P: return 'p'; case SIM_KEY_Q: return 'q'; case SIM_KEY_R: return 'r'; case SIM_KEY_S: return 's'; case SIM_KEY_T: return 't'; case SIM_KEY_U: return 'u'; case SIM_KEY_V: return 'v'; case SIM_KEY_W: return 'w'; case SIM_KEY_X: return 'x'; case SIM_KEY_Y: return 'y'; case SIM_KEY_Z: return 'z'; case SIM_KEY_BACKQUOTE: return '`'; case SIM_KEY_MINUS: return '-'; case SIM_KEY_EQUALS: return '='; case SIM_KEY_LEFT_BRACKET: return '['; case SIM_KEY_RIGHT_BRACKET: return ']'; case SIM_KEY_SEMICOLON: return ';'; case SIM_KEY_SINGLE_QUOTE: return '\''; case SIM_KEY_BACKSLASH: return '\\'; case SIM_KEY_LEFT_BACKSLASH: return '\\'; case SIM_KEY_COMMA: return ','; case SIM_KEY_PERIOD: return '.'; case SIM_KEY_SLASH: return '/'; case SIM_KEY_BACKSPACE: return '\b'; case SIM_KEY_TAB: return '\t'; case SIM_KEY_ENTER: return '\r'; case SIM_KEY_SPACE: return ' '; } return k; } static void key_to_ascii (SIM_KEY_EVENT *kev) { static t_bool k_ctrl, k_shift, k_alt, k_win; #define MODKEY(L, R, mod) \ case L: case R: mod = (kev->state != SIM_KEYPRESS_UP); break; #define MODIFIER_KEYS \ MODKEY(SIM_KEY_ALT_L, SIM_KEY_ALT_R, k_alt) \ MODKEY(SIM_KEY_CTRL_L, SIM_KEY_CTRL_R, k_ctrl) \ MODKEY(SIM_KEY_SHIFT_L, SIM_KEY_SHIFT_R, k_shift) \ MODKEY(SIM_KEY_WIN_L, SIM_KEY_WIN_R, k_win) #define SPCLKEY(K, LC, UC) \ case K: \ if (kev->state != SIM_KEYPRESS_UP) \ display_last_char = (unsigned char)(k_shift ? UC : LC);\ break; #define SPECIAL_CHAR_KEYS \ SPCLKEY(SIM_KEY_BACKQUOTE, '`', '~') \ SPCLKEY(SIM_KEY_MINUS, '-', '_') \ SPCLKEY(SIM_KEY_EQUALS, '=', '+') \ SPCLKEY(SIM_KEY_LEFT_BRACKET, '[', '{') \ SPCLKEY(SIM_KEY_RIGHT_BRACKET, ']', '}') \ SPCLKEY(SIM_KEY_SEMICOLON, ';', ':') \ SPCLKEY(SIM_KEY_SINGLE_QUOTE, '\'', '"') \ SPCLKEY(SIM_KEY_LEFT_BACKSLASH, '\\', '|') \ SPCLKEY(SIM_KEY_COMMA, ',', '<') \ SPCLKEY(SIM_KEY_PERIOD, '.', '>') \ SPCLKEY(SIM_KEY_SLASH, '/', '?') \ SPCLKEY(SIM_KEY_ESC, '\033', '\033') \ SPCLKEY(SIM_KEY_BACKSPACE, '\177', '\177') \ SPCLKEY(SIM_KEY_TAB, '\t', '\t') \ SPCLKEY(SIM_KEY_ENTER, '\r', '\r') \ SPCLKEY(SIM_KEY_SPACE, ' ', ' ') switch (kev->key) { MODIFIER_KEYS SPECIAL_CHAR_KEYS case SIM_KEY_0: case SIM_KEY_1: case SIM_KEY_2: case SIM_KEY_3: case SIM_KEY_4: case SIM_KEY_5: case SIM_KEY_6: case SIM_KEY_7: case SIM_KEY_8: case SIM_KEY_9: if (kev->state != SIM_KEYPRESS_UP) display_last_char = (unsigned char)('0' + (kev->key - SIM_KEY_0)); break; case SIM_KEY_A: case SIM_KEY_B: case SIM_KEY_C: case SIM_KEY_D: case SIM_KEY_E: case SIM_KEY_F: case SIM_KEY_G: case SIM_KEY_H: case SIM_KEY_I: case SIM_KEY_J: case SIM_KEY_K: case SIM_KEY_L: case SIM_KEY_M: case SIM_KEY_N: case SIM_KEY_O: case SIM_KEY_P: case SIM_KEY_Q: case SIM_KEY_R: case SIM_KEY_S: case SIM_KEY_T: case SIM_KEY_U: case SIM_KEY_V: case SIM_KEY_W: case SIM_KEY_X: case SIM_KEY_Y: case SIM_KEY_Z: if (kev->state != SIM_KEYPRESS_UP) display_last_char = (unsigned char)((kev->key - SIM_KEY_A) + (k_ctrl ? 1 : (k_shift ? 'A' : 'a'))); break; } } int ws_poll(int *valp, int maxus) { SIM_MOUSE_EVENT mev; SIM_KEY_EVENT kev; if (maxus > 1000) sim_os_ms_sleep (maxus/1000); if (SCPE_OK == vid_poll_mouse (&mev)) { unsigned char old_lp_sw = display_lp_sw; if ((display_lp_sw = mev.b1_state)) { ws_lp_x = mev.x_pos; ws_lp_y = (ypixels - 1) - mev.y_pos; /* range 0 - (ypixels-1) */ /* convert to display coordinates */ ws_lp_x /= pix_size; ws_lp_y /= pix_size; if (!old_lp_sw && !display_tablet) vid_set_cursor (1, cross_cursor->width, cross_cursor->height, cross_cursor->data, cross_cursor->mask, cross_cursor->hot_x, cross_cursor->hot_y); } else { ws_lp_x = ws_lp_y = -1; if (old_lp_sw && !display_tablet) vid_set_cursor (1, arrow_cursor->width, arrow_cursor->height, arrow_cursor->data, arrow_cursor->mask, arrow_cursor->hot_x, arrow_cursor->hot_y); } vid_set_cursor_position (mev.x_pos, mev.y_pos); } if (SCPE_OK == vid_poll_kb (&kev)) { if ((vid_display_kb_event_process == NULL) || (vid_display_kb_event_process (&kev) != 0)) { switch (kev.state) { case SIM_KEYPRESS_DOWN: case SIM_KEYPRESS_REPEAT: display_keydown(map_key(kev.key)); break; case SIM_KEYPRESS_UP: display_keyup(map_key(kev.key)); break; } key_to_ascii (&kev); } } return 1; } /* XPM */ static const char *arrow[] = { /* width height num_colors chars_per_pixel */ " 16 16 3 1", /* colors */ "X c #000000", /* black */ ". c #ffffff", /* white */ " c None", /* pixels */ "X ", "XX ", "X.X ", "X..X ", "X...X ", "X....X ", "X.....X ", "X......X ", "X.......X ", "X........X ", "X.....XXXXX ", "X..X..X ", "X.X X..X ", "XX X..X ", "X X..X ", " XX ", }; /* XPM */ static const char *cross[] = { /* width height num_colors chars_per_pixel hot_x hot_y*/ " 16 16 3 1 7 7", /* colors */ "X c #000000", /* black */ ". c #ffffff", /* white */ " c None", /* pixels */ " XXXX ", " X..X ", " X..X ", " X..X ", " X..X ", " X..X ", "XXXXXXX..XXXXXXX", "X..............X", "X..............X", "XXXXXXX..XXXXXXX", " X..X ", " X..X ", " X..X ", " X..X ", " X..X ", " XXXX ", "7,7" }; static CURSOR *ws_create_cursor(const char *image[]) { int byte, bit, row, col; Uint8 *data = NULL; Uint8 *mask = NULL; char black, white, transparent; CURSOR *result = NULL; int width, height, colors, cpp; int hot_x = 0, hot_y = 0; if (4 > sscanf(image[0], "%d %d %d %d %d %d", &width, &height, &colors, &cpp, &hot_x, &hot_y)) return result; if ((cpp != 1) || (0 != width%8) || (colors != 3)) return result; black = image[1][0]; white = image[2][0]; transparent = image[3][0]; data = (Uint8 *)calloc (1, (width / 8) * height); mask = (Uint8 *)calloc (1, (width / 8) * height); if (!data || !mask) { free (data); free (mask); return result; } bit = 7; byte = 0; for (row=0; rowdata = data; result->mask = mask; result->width = width; result->height = height; result->hot_x = hot_x; result->hot_y = hot_y; } else { free (data); free (mask); } return result; } static void ws_free_cursor (CURSOR *cursor) { if (!cursor) return; free (cursor->data); free (cursor->mask); free (cursor); } /* called from display layer on first display op */ int ws_init(const char *name, int xp, int yp, int colors, void *dptr) { int i; int ret; arrow_cursor = ws_create_cursor (arrow); cross_cursor = ws_create_cursor (cross); xpixels = xp; ypixels = yp; window_name = name; surface = (uint32 *)realloc (surface, xpixels*ypixels*sizeof(*surface)); ret = (0 == vid_open ((DEVICE *)dptr, name, xp*pix_size, yp*pix_size, 0)); if (ret) vid_set_cursor (1, arrow_cursor->width, arrow_cursor->height, arrow_cursor->data, arrow_cursor->mask, arrow_cursor->hot_x, arrow_cursor->hot_y); ws_palette[0] = vid_map_rgb (0x00, 0x00, 0x00); /* black */ ws_palette[1] = vid_map_rgb (0xFF, 0xFF, 0xFF); /* white */ for (i=0; i> 8) & 0xFF, (g >> 8) & 0xFF, (b >> 8) & 0xFF); for (i=0; i xpixels || y > ypixels) return; y = ypixels - 1 - y; /* invert y, top left origin */ if (brush == NULL) brush = (uint32 *)ws_color_black (); if (pix_size > 1) { int i, j; for (i=0; i