500 lines
16 KiB
C
500 lines
16 KiB
C
/*
|
|
* simh sim_video support for XY display simulator
|
|
* Mark Pizzolato <mark@infocomm.com>
|
|
* 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 <stdio.h>
|
|
#include <stdlib.h>
|
|
#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; row<height; ++row) {
|
|
for (col=0; col<width; ++col) {
|
|
if (image[colors+1+row][col] == black) {
|
|
data[byte] |= (1 << bit);
|
|
mask[byte] |= (1 << bit);
|
|
}
|
|
else
|
|
if (image[colors+1+row][col] == white) {
|
|
mask[byte] |= (1 << bit);
|
|
}
|
|
else
|
|
if (image[colors+1+row][col] != transparent) {
|
|
free (data);
|
|
free (mask);
|
|
return result;
|
|
}
|
|
--bit;
|
|
if (bit < 0) {
|
|
++byte;
|
|
bit = 7;
|
|
}
|
|
}
|
|
}
|
|
result = (CURSOR *)calloc (1, sizeof(*result));
|
|
if (result) {
|
|
result->data = 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<xpixels*ypixels; i++)
|
|
surface[i] = ws_palette[0];
|
|
return ret;
|
|
}
|
|
|
|
void
|
|
ws_shutdown(void)
|
|
{
|
|
ws_free_cursor(arrow_cursor);
|
|
ws_free_cursor(cross_cursor);
|
|
vid_close();
|
|
}
|
|
|
|
void *
|
|
ws_color_rgb(int r, int g, int b)
|
|
{
|
|
uint32 color, i;
|
|
|
|
color = vid_map_rgb ((r >> 8) & 0xFF, (g >> 8) & 0xFF, (b >> 8) & 0xFF);
|
|
for (i=0; i<ncolors; i++) {
|
|
if (colors[i] == color)
|
|
return &colors[i];
|
|
}
|
|
if (ncolors == size_colors) {
|
|
colors = (uint32 *)realloc (colors, (ncolors + 1000) * sizeof (*colors));
|
|
size_colors += 1000;
|
|
if (size_colors == 1000) {
|
|
colors[0] = ws_palette[0];
|
|
colors[1] = ws_palette[1];
|
|
ncolors = 2;
|
|
}
|
|
}
|
|
colors[ncolors] = color;
|
|
++ncolors;
|
|
return (void *)&colors[ncolors-1];
|
|
}
|
|
|
|
void *
|
|
ws_color_black(void)
|
|
{
|
|
return (void *)&ws_palette[0];
|
|
}
|
|
|
|
void *
|
|
ws_color_white(void)
|
|
{
|
|
return (void *)&ws_palette[1];
|
|
}
|
|
|
|
void
|
|
ws_display_point(int x, int y, void *color)
|
|
{
|
|
uint32 *brush = (uint32 *)color;
|
|
|
|
if (x > 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<pix_size; i++)
|
|
for (j=0; j<pix_size; j++)
|
|
surface[(y + i)*xpixels + x + j] = *brush;
|
|
}
|
|
else
|
|
surface[y*xpixels + x] = *brush;
|
|
}
|
|
|
|
void
|
|
ws_sync(void) {
|
|
vid_draw (0, 0, xpixels, ypixels, surface);
|
|
vid_refresh ();
|
|
}
|
|
|
|
void
|
|
ws_beep(void) {
|
|
vid_beep ();
|
|
}
|
|
|
|
unsigned long
|
|
os_elapsed(void)
|
|
{
|
|
static int tnew;
|
|
unsigned long ret;
|
|
static uint32 t[2];
|
|
|
|
t[tnew] = sim_os_msec();
|
|
if (t[!tnew] == 0)
|
|
ret = ~0L; /* +INF */
|
|
else
|
|
ret = (t[tnew] - t[!tnew]) * 1000;/* usecs */
|
|
tnew = !tnew; /* Ecclesiastes III */
|
|
return ret;
|
|
}
|