diff options
author | Jordi Vilalta Prat | 2009-02-15 06:10:59 +0000 |
---|---|---|
committer | Jordi Vilalta Prat | 2009-02-15 06:10:59 +0000 |
commit | fa6e10e9cec163845aa29e7940c86e9c9ab8a2bc (patch) | |
tree | ce87338830cc8c149e1de545246bcefe4f45da00 /engines/sci/gfx/drivers | |
parent | 7c148ddf021c990fa866b7600f979aac9a5b26c9 (diff) | |
download | scummvm-rg350-fa6e10e9cec163845aa29e7940c86e9c9ab8a2bc.tar.gz scummvm-rg350-fa6e10e9cec163845aa29e7940c86e9c9ab8a2bc.tar.bz2 scummvm-rg350-fa6e10e9cec163845aa29e7940c86e9c9ab8a2bc.zip |
Import the SCI engine sources from the FreeSCI Glutton branch (it doesn't compile yet)
svn-id: r38192
Diffstat (limited to 'engines/sci/gfx/drivers')
-rw-r--r-- | engines/sci/gfx/drivers/EXPORTS_ggi | 1 | ||||
-rw-r--r-- | engines/sci/gfx/drivers/EXPORTS_sdl | 1 | ||||
-rw-r--r-- | engines/sci/gfx/drivers/EXPORTS_xlib | 2 | ||||
-rw-r--r-- | engines/sci/gfx/drivers/Makefile.am | 32 | ||||
-rw-r--r-- | engines/sci/gfx/drivers/dc_driver.c | 1267 | ||||
-rw-r--r-- | engines/sci/gfx/drivers/dd_driver.cpp | 1021 | ||||
-rw-r--r-- | engines/sci/gfx/drivers/dd_driver.h | 2 | ||||
-rw-r--r-- | engines/sci/gfx/drivers/dd_driver_line.cpp | 163 | ||||
-rw-r--r-- | engines/sci/gfx/drivers/directfb_driver.c | 1033 | ||||
-rw-r--r-- | engines/sci/gfx/drivers/dx_driver.cpp | 1179 | ||||
-rw-r--r-- | engines/sci/gfx/drivers/dx_driver.h | 151 | ||||
-rw-r--r-- | engines/sci/gfx/drivers/gfx_drivers.c | 192 | ||||
-rw-r--r-- | engines/sci/gfx/drivers/ggi_driver.c | 1035 | ||||
-rw-r--r-- | engines/sci/gfx/drivers/null_driver.c | 211 | ||||
-rw-r--r-- | engines/sci/gfx/drivers/scummvm_driver.cpp | 543 | ||||
-rw-r--r-- | engines/sci/gfx/drivers/sdl_driver.c | 1235 | ||||
-rw-r--r-- | engines/sci/gfx/drivers/xlib_driver.c | 1460 |
17 files changed, 9528 insertions, 0 deletions
diff --git a/engines/sci/gfx/drivers/EXPORTS_ggi b/engines/sci/gfx/drivers/EXPORTS_ggi new file mode 100644 index 0000000000..0f9ed580f6 --- /dev/null +++ b/engines/sci/gfx/drivers/EXPORTS_ggi @@ -0,0 +1 @@ +gfx_driver_ggi diff --git a/engines/sci/gfx/drivers/EXPORTS_sdl b/engines/sci/gfx/drivers/EXPORTS_sdl new file mode 100644 index 0000000000..55e1950033 --- /dev/null +++ b/engines/sci/gfx/drivers/EXPORTS_sdl @@ -0,0 +1 @@ +gfx_driver_sdl diff --git a/engines/sci/gfx/drivers/EXPORTS_xlib b/engines/sci/gfx/drivers/EXPORTS_xlib new file mode 100644 index 0000000000..5d705799b0 --- /dev/null +++ b/engines/sci/gfx/drivers/EXPORTS_xlib @@ -0,0 +1,2 @@ +gfx_driver_xlib + diff --git a/engines/sci/gfx/drivers/Makefile.am b/engines/sci/gfx/drivers/Makefile.am new file mode 100644 index 0000000000..c5dbbe0e9f --- /dev/null +++ b/engines/sci/gfx/drivers/Makefile.am @@ -0,0 +1,32 @@ +EXTRA_DIST = dc_driver.c dd_driver.cpp dd_driver.h dd_driver_line.cpp dx_driver.cpp dx_driver.h +INCLUDES = -I$(top_srcdir)/src/include @ac_ggi_includes@ @ac_png_includes@ @X_CFLAGS@ @EXTRA_INCLUDES@ +AM_CFLAGS = $(SDL_CFLAGS) +LDADD = $(LDADD_ALL) + +gfxdriverdir = $(libdir)/freesci/gfx + +#EXTRA_LTLIBRARIES = xlib_driver.la ggi_driver.la sdl_driver.la +#gfxdriver_LTLIBRARIES = @fsci_xlib_dl@ @fsci_ggi_dl@ @fsci_sdl_dl@ + +noinst_LIBRARIES = libscidrivers.a +libscidrivers_a_SOURCES = gfx_drivers.c xlib_driver.c ggi_driver.c sdl_driver.c directfb_driver.c \ + null_driver.c +#libscidrivers_a_OBJECTS = gfx_drivers.o @fsci_xlib_st@ @fsci_ggi_st@ @fsci_sdl_st@ + + +#xlib_driver_la_LDFLAGS = -rpath $(gfxdriverdir) -module -no-undefined \ +# -export-symbols $(srcdir)/EXPORTS_xlib +#xlib_driver_la_LIBADD = @X_LIBS@ +#xlib_driver_la_SOURCES = xlib_driver.c + +#ggi_driver_la_LDFLAGS = -rpath $(gfxdriverdir) -module -no-undefined \ +# -export-symbols $(srcdir)/EXPORTS_ggi +#ggi_driver_la_LIBADD = @X_LIBS@ @ac_ggi_libraries@ +#ggi_driver_la_SOURCES = ggi_driver.c + +#sdl_driver_la_LDFLAGS = -rpath $(gfxdriverdir) -module -no-undefined \ +# -export-symbols $(srcdir)/EXPORTS_sdl +#sdl_driver_la_LIBADD = @SDL_LIBS@ +#3sdl_driver_la_SOURCES = sdl_driver.c + + diff --git a/engines/sci/gfx/drivers/dc_driver.c b/engines/sci/gfx/drivers/dc_driver.c new file mode 100644 index 0000000000..5f2b4419ae --- /dev/null +++ b/engines/sci/gfx/drivers/dc_driver.c @@ -0,0 +1,1267 @@ +/*************************************************************************** + dc_driver.c Copyright (C) 2002-2005 Walter van Niftrik + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Walter van Niftrik <w.f.b.w.v.niftrik@stud.tue.nl> + +***************************************************************************/ + +#include <kos/thread.h> +#include <kos/sem.h> +#include <dc/maple.h> +#include <dc/maple/mouse.h> +#include <dc/maple/keyboard.h> +#include <dc/maple/controller.h> +#include <dc/pvr.h> + +#include <sci_memory.h> +#include <gfx_driver.h> +#include <gfx_tools.h> + +#include "keyboard.h" + +/* Event queue struct */ + +struct dc_event_t { + sci_event_t event; + struct dc_event_t *next; +}; + +#define SCI_DC_RENDER_PVR (1 << 0) /* 0 = VRAM rendering, 1 = PVR */ +#define SCI_DC_REFRESH_50HZ (1 << 1) /* 0 = 60Hz refresh rate, 1 = 50Hz */ + +static int flags = 0; + +struct _dc_state { + /* 0 = static buffer, 1 = back buffer, 2 = front buffer */ + /* Visual maps */ + byte *visual[3]; + /* Priority maps */ + byte *priority[2]; + /* Line pitch of visual buffers */ + int line_pitch[3]; + + /* PVR only */ + /* Polygon header */ + pvr_poly_hdr_t pvr_hdr; + /* Polygon header for virtual keyboard */ + pvr_poly_hdr_t pvr_hdr_vkbd; + /* Texture for virtual keyboard */ + uint16 *vkbd_txr; + + /* Pointers to first and last event in the event queue */ + struct dc_event_t *first_event; + struct dc_event_t *last_event; + + /* Semaphores for mouse pointer location and event queue updates */ + semaphore_t *sem_event, *sem_pointer; + + /* The dx and dy of the mouse pointer since last get_event() */ + int pointer_dx, pointer_dy; + + /* The current bucky state of the keys */ + int buckystate; + + /* Thread ID of the input thread */ + kthread_t *thread; + + /* Flag to stop the input thread. (!0 = run, 0 = stop) */ + int run_thread; + + /* Controller key repeat timer */ + int timer; + + /* Controller state */ + unsigned int cont_state; + + /* Virtual keyboard on/off flag */ + int vkbd; +}; + +#define S ((struct _dc_state *)(drv->state)) + +#define XFACT drv->mode->xfact +#define YFACT drv->mode->yfact +#define BYTESPP drv->mode->bytespp + +#define DC_MOUSE_LEFT (1<<0) +#define DC_MOUSE_RIGHT (1<<1) + +#define DC_KEY_CAPSLOCK (1<<0) +#define DC_KEY_NUMLOCK (1<<1) +#define DC_KEY_SCRLOCK (1<<2) +#define DC_KEY_INSERT (1<<3) + +static void +pvr_init_gfx(struct _gfx_driver *drv, int xfact, int yfact, int bytespp) +/* Initialises the graphics driver for PVR rendering mode +** Parameters: (_gfx_driver *) drv: The driver to use +** (int) xfact, yfact: X and Y scaling factors. (xfact, yfact) +** has to be either (1, 1) or (2, 2). +** (int) bytespp: The display depth in bytes per pixel. Must be 2. +** Returns : void +*/ +{ + pvr_poly_cxt_t cxt; + + /* Initialize PVR to defaults and set background color to black */ + + if (flags & SCI_DC_REFRESH_50HZ) + vid_set_mode(DM_640x480_PAL_IL, PM_RGB565); + else + vid_set_mode(DM_640x480, PM_RGB565); + + pvr_init_defaults(); + pvr_set_bg_color(0,0,0); + + /* Allocate and initialize texture RAM */ + S->visual[2] = pvr_mem_malloc(512*256*bytespp*xfact*yfact); + S->line_pitch[2] = 512*bytespp*xfact; + memset(S->visual[2], 0, 512*256*bytespp*xfact*yfact); + + /* Create textured polygon context */ + pvr_poly_cxt_txr(&cxt, PVR_LIST_OP_POLY, PVR_TXRFMT_RGB565 | + PVR_TXRFMT_NONTWIDDLED, 512*xfact, 256*yfact, S->visual[2], 0); + + /* Create polygon header from context */ + pvr_poly_compile(&(S->pvr_hdr), &cxt); + + /* Allocate and initialize texture RAM for virtual keyboard */ + S->vkbd_txr = pvr_mem_malloc(512*64*bytespp*xfact*yfact); + memset(S->vkbd_txr, 0, 512*64*bytespp*xfact*yfact); + + /* Create textured polygon context for virtual keyboard */ + pvr_poly_cxt_txr(&cxt, PVR_LIST_TR_POLY, PVR_TXRFMT_RGB565 | + PVR_TXRFMT_NONTWIDDLED, 512*xfact, 64*yfact, S->vkbd_txr, 0); + + /* Create polygon header from context for virtual keyboard */ + pvr_poly_compile(&(S->pvr_hdr_vkbd), &cxt); + + vkbd_init((uint16 *) S->vkbd_txr, 512); + vkbd_draw(); +} + +static void +pvr_do_frame(struct _gfx_driver *drv) +/* Renders a frame for PVR rendering mode +** Parameters: (_gfx_driver *) drv: The driver to use +** Returns : void +*/ +{ + pvr_vertex_t vert; + + /* Wait until we can send another frame to the PVR */ + pvr_wait_ready(); + + /* Start a new scene */ + pvr_scene_begin(); + + /* Start an opaque polygon list */ + pvr_list_begin(PVR_LIST_OP_POLY); + + /* Submit polygon header */ + pvr_prim(&(S->pvr_hdr), sizeof(S->pvr_hdr)); + + /* Create and submit vertices */ + vert.flags = PVR_CMD_VERTEX; + vert.x = 0.0f; + vert.y = 480.0f; + vert.z = 1.0f; + vert.u = 0.0f; + vert.v = 0.78125f; + vert.argb = PVR_PACK_COLOR(1.0f, 1.0f, 1.0f, 1.0f); + vert.oargb = 0; + pvr_prim(&vert, sizeof(vert)); + + vert.y = 0.0f; + vert.v = 0.0f; + pvr_prim(&vert, sizeof(vert)); + + vert.x = 640.0f; + vert.y = 480.0f; + vert.u = 0.625f; + vert.v = 0.78125f; + pvr_prim(&vert, sizeof(vert)); + + vert.flags = PVR_CMD_VERTEX_EOL; + vert.y = 0.0f; + vert.v = 0.0f; + pvr_prim(&vert, sizeof(vert)); + + /* End list */ + pvr_list_finish(); + + /* Display virtual keyboard */ + if (S->vkbd) { + /* Start an translucent polygon list */ + pvr_list_begin(PVR_LIST_TR_POLY); + + /* Submit polygon header */ + pvr_prim(&(S->pvr_hdr_vkbd), sizeof(S->pvr_hdr_vkbd)); + + /* Create and submit vertices */ + vert.flags = PVR_CMD_VERTEX; + vert.x = 0.0f; + vert.y = 480.0f; + vert.z = 1.0f; + vert.u = 0.0f; + vert.v = 0.625f; + vert.argb = PVR_PACK_COLOR(0.8f, 1.0f, 1.0f, 1.0f); + vert.oargb = 0; + pvr_prim(&vert, sizeof(vert)); + + vert.y = 400.0f; + vert.v = 0.0f; + pvr_prim(&vert, sizeof(vert)); + + vert.x = 640.0f; + vert.y = 480.0f; + vert.u = 0.625f; + vert.v = 0.625f; + pvr_prim(&vert, sizeof(vert)); + + vert.flags = PVR_CMD_VERTEX_EOL; + vert.y = 400.0f; + vert.v = 0.0f; + pvr_prim(&vert, sizeof(vert)); + + /* End list */ + pvr_list_finish(); + } + + /* End scene */ + pvr_scene_finish(); +} + +static void +vram_init_gfx(struct _gfx_driver *drv, int xfact, int yfact, int bytespp) +/* Initialises the graphics driver for VRAM rendering mode +** Parameters: (_gfx_driver *) drv: The driver to use +** (int) xfact, yfact: X and Y scaling factors. (xfact, yfact) +** has to be either (1, 1) or (2, 2). +** (int) bytespp: The display depth in bytes per pixel. Must be +** either 2 or 4. +** Returns : void +*/ +{ + int vidres = 0, vidcol = 0; + + /* Center screen vertically */ + S->visual[2] = (byte *) vram_s+320*xfact*20*yfact*bytespp; + + S->line_pitch[2] = 320*xfact*bytespp; + + memset(S->visual[2], 0, 320*xfact*240*yfact*bytespp); + + switch(bytespp) { + case 2: + vidcol = PM_RGB565; + break; + case 4: + vidcol = PM_RGB888; + } + + if (flags & SCI_DC_REFRESH_50HZ) switch (xfact) { + case 1: + vidres = DM_320x240_PAL; + break; + case 2: + vidres = DM_640x480_PAL_IL; + } + else switch (xfact) { + case 1: + vidres = DM_320x240; + break; + case 2: + vidres = DM_640x480; + } + + vid_set_mode(vidres, vidcol); + + vkbd_init((uint16 *) (S->visual[2] + 320 * xfact * 200 * yfact * bytespp), 320); +} + +static void +vram_hide_keyboard(struct _gfx_driver *drv) +/* Hides the virtual keyboard in VRAM rendering mode +** Parameters: (_gfx_driver *) drv: The driver to use +** Returns : void +*/ +{ + vid_set_start(0); + memset4(S->visual[2] + 320 * XFACT * 200 * YFACT * BYTESPP, 0, 320 * XFACT * 40 * YFACT * BYTESPP); +} + +static void +vram_show_keyboard(struct _gfx_driver *drv) +/* Displays the virtual keyboard in VRAM rendering mode +** Parameters: (_gfx_driver *) drv: The driver to use +** Returns : void +*/ +{ + vid_set_start(320 * XFACT * 20 * YFACT * BYTESPP); + vkbd_draw(); +} + +static int +dc_add_event(struct _gfx_driver *drv, sci_event_t *event) +/* Adds an event to the end of an event queue +** Parameters: (_gfx_driver *) drv: The driver to use +** (sci_event_t *) event: The event to add +** Returns : (int) 1 on success, 0 on error +*/ +{ + struct dc_event_t *dc_event; + if (!(dc_event = sci_malloc(sizeof(dc_event)))) { + printf("Error: Could not reserve memory for event\n"); + return 0; + } + + dc_event->event = *event; + dc_event->next = NULL; + + /* Semaphore prevents get_event() from removing the last event in + ** the event queue while a next event is being attached to it. + */ + + sem_wait(S->sem_event); + if (!(S->last_event)) { + /* Event queue is empty */ + S->first_event = dc_event; + S->last_event = dc_event; + sem_signal(S->sem_event); + return 1; + } + + S->last_event->next = dc_event; + S->last_event = dc_event; + sem_signal(S->sem_event); + return 1; +} + +static int +dc_map_key(int *keystate, uint8 key) +/* Converts a kos keycode to a freesci keycode. This function also adjusts +** the caps lock, num lock, scroll lock and insert states in keystate. +** Parameters: (int *) keystate: Pointer to the keystate variable +** (uint8) key: The kos keycode to convert +** Returns : (int) Converted freesci keycode on success, 0 on error +*/ +{ + if ((key >= KBD_KEY_A) && (key <= KBD_KEY_Z)) + return 'a' + (key - KBD_KEY_A); + + if ((key >= KBD_KEY_1) && (key <= KBD_KEY_9)) + return '1' + (key - KBD_KEY_1); + + switch (key) { + case KBD_KEY_0: return '0'; + case KBD_KEY_BACKSPACE: return SCI_K_BACKSPACE; + case KBD_KEY_TAB: return 9; + case KBD_KEY_ESCAPE: return SCI_K_ESC; + case KBD_KEY_ENTER: + case KBD_KEY_PAD_ENTER: return SCI_K_ENTER; + case KBD_KEY_DEL: + case KBD_KEY_PAD_PERIOD: return SCI_K_DELETE; + case KBD_KEY_INSERT: + case KBD_KEY_PAD_0: *keystate ^= DC_KEY_INSERT; + return SCI_K_INSERT; + case KBD_KEY_END: + case KBD_KEY_PAD_1: return SCI_K_END; + case KBD_KEY_DOWN: + case KBD_KEY_PAD_2: return SCI_K_DOWN; + case KBD_KEY_PGDOWN: + case KBD_KEY_PAD_3: return SCI_K_PGDOWN; + case KBD_KEY_LEFT: + case KBD_KEY_PAD_4: return SCI_K_LEFT; + case KBD_KEY_PAD_5: return SCI_K_CENTER; + case KBD_KEY_RIGHT: + case KBD_KEY_PAD_6: return SCI_K_RIGHT; + case KBD_KEY_HOME: + case KBD_KEY_PAD_7: return SCI_K_HOME; + case KBD_KEY_UP: + case KBD_KEY_PAD_8: return SCI_K_UP; + case KBD_KEY_PGUP: + case KBD_KEY_PAD_9: return SCI_K_PGUP; + case KBD_KEY_F1: return SCI_K_F1; + case KBD_KEY_F2: return SCI_K_F2; + case KBD_KEY_F3: return SCI_K_F3; + case KBD_KEY_F4: return SCI_K_F4; + case KBD_KEY_F5: return SCI_K_F5; + case KBD_KEY_F6: return SCI_K_F6; + case KBD_KEY_F7: return SCI_K_F7; + case KBD_KEY_F8: return SCI_K_F8; + case KBD_KEY_F9: return SCI_K_F9; + case KBD_KEY_F10: return SCI_K_F10; + case KBD_KEY_PAD_PLUS: return '+'; + case KBD_KEY_SLASH: + case KBD_KEY_PAD_DIVIDE: return '/'; + case KBD_KEY_MINUS: + case KBD_KEY_PAD_MINUS: return '-'; + case KBD_KEY_PAD_MULTIPLY: return '*'; + case KBD_KEY_COMMA: return ','; + case KBD_KEY_PERIOD: return '.'; + case KBD_KEY_BACKSLASH: return '\\'; + case KBD_KEY_SEMICOLON: return ';'; + case KBD_KEY_QUOTE: return '\''; + case KBD_KEY_LBRACKET: return '['; + case KBD_KEY_RBRACKET: return ']'; + case KBD_KEY_TILDE: return '`'; + case KBD_KEY_PLUS: return '='; + case KBD_KEY_SPACE: return ' '; + case KBD_KEY_CAPSLOCK: *keystate ^= DC_KEY_CAPSLOCK; + return 0; + case KBD_KEY_SCRLOCK: *keystate ^= DC_KEY_SCRLOCK; + return 0; + case KBD_KEY_PAD_NUMLOCK: *keystate ^= DC_KEY_NUMLOCK; + return 0; + } + + printf("Warning: Unmapped key: %02x\n", key); + + return 0; +} + +static void +dc_input_thread(struct _gfx_driver *drv) +/* Thread that checks the dreamcast keyboard and mouse states. It adds +** keypresses and mouseclicks to the end of the event queue. It also updates +** drv->state.buckystate and drv->state.pointer_dx/dy. +** Parameters: (_gfx_driver *) drv: The driver to use +** Returns : void +*/ +{ + /* State of mouse buttons */ + unsigned int mstate = 0; + /* Last key pressed */ + unsigned int lastkey = KBD_KEY_NONE; + /* State of caps lock, scroll lock, num lock and insert keys */ + int keystate = DC_KEY_INSERT; + + while (S->run_thread) { + maple_device_t *kaddr = NULL, *maddr, *caddr; + mouse_state_t *mouse; + kbd_state_t *kbd = 0; + cont_state_t *cont; + uint8 key; + int skeys; + int bucky = 0, vkbd_bucky = 0; + sci_event_t event; + + if (!(flags & SCI_DC_RENDER_PVR)) + /* Sleep for 10ms */ + thd_sleep(10); + else + pvr_do_frame(drv); + + /* Keyboard handling */ + /* Experimental workaround for the Mad Catz adapter problem */ + if (!kaddr) + kaddr = maple_enum_type(0, MAPLE_FUNC_KEYBOARD); + if (kaddr && (kbd = maple_dev_status(kaddr))) { + key = kbd->cond.keys[0]; + skeys = kbd->shift_keys; + + bucky = ((skeys & (KBD_MOD_LCTRL | KBD_MOD_RCTRL))? + SCI_EVM_CTRL : 0) | + ((skeys & (KBD_MOD_LALT | KBD_MOD_RALT))? + SCI_EVM_ALT : 0) | + ((skeys & KBD_MOD_LSHIFT)? + SCI_EVM_LSHIFT : 0) | + ((skeys & KBD_MOD_RSHIFT)? + SCI_EVM_RSHIFT : 0) | + ((keystate & DC_KEY_NUMLOCK)? + SCI_EVM_NUMLOCK : 0) | + ((keystate & DC_KEY_SCRLOCK)? + SCI_EVM_SCRLOCK : 0) | + ((keystate & DC_KEY_INSERT)? + SCI_EVM_INSERT : 0); + + /* If a shift key is pressed when caps lock is on, set + ** both shift key states to 0. If no shift keys are + ** pressed when caps lock is on, set both shift key + ** states to 1 + */ + + if (keystate & DC_KEY_CAPSLOCK) { + if ((bucky & SCI_EVM_LSHIFT) || + (bucky & SCI_EVM_RSHIFT)) + bucky &= + ~(SCI_EVM_LSHIFT | SCI_EVM_RSHIFT); + else bucky |= SCI_EVM_LSHIFT | SCI_EVM_RSHIFT; + } + + if ((key != lastkey) && (key != KBD_KEY_NONE)) { + event.type = SCI_EVT_KEYBOARD; + event.data = dc_map_key(&keystate, key); + event.buckybits = bucky | vkbd_bucky; + if (event.data) dc_add_event(drv, &event); + } + lastkey = key; + } + else kaddr = NULL; + + /* Mouse handling */ + if ((maddr = maple_enum_type(0, MAPLE_FUNC_MOUSE)) && + (mouse = maple_dev_status(maddr))) { + + /* Enable mouse support */ + drv->capabilities |= GFX_CAPABILITY_MOUSE_SUPPORT; + + /* Semaphore prevents get_event() from accessing + ** S->pointer_dx/dy while they are being updated + */ + sem_wait(S->sem_pointer); + S->pointer_dx += mouse->dx; + S->pointer_dy += mouse->dy; + sem_signal(S->sem_pointer); + + if ((mouse->buttons & MOUSE_LEFTBUTTON) && + !(mstate & DC_MOUSE_LEFT)) { + event.type = SCI_EVT_MOUSE_PRESS; + event.data = 1; + event.buckybits = bucky | vkbd_bucky; + dc_add_event(drv, &event); + mstate |= DC_MOUSE_LEFT; + } + if ((mouse->buttons & MOUSE_RIGHTBUTTON) && + !(mstate & DC_MOUSE_RIGHT)) { + event.type = SCI_EVT_MOUSE_PRESS; + event.data = 2; + event.buckybits = bucky | vkbd_bucky; + dc_add_event(drv, &event); + mstate |= DC_MOUSE_RIGHT; + } + if (!(mouse->buttons & MOUSE_LEFTBUTTON) && + (mstate & DC_MOUSE_LEFT)) { + event.type = SCI_EVT_MOUSE_RELEASE; + event.data = 1; + event.buckybits = bucky | vkbd_bucky; + dc_add_event(drv, &event); + mstate &= ~DC_MOUSE_LEFT; + } + if (!(mouse->buttons & MOUSE_RIGHTBUTTON) && + (mstate & DC_MOUSE_RIGHT)) { + event.type = SCI_EVT_MOUSE_RELEASE; + event.data = 2; + event.buckybits = bucky | vkbd_bucky; + dc_add_event(drv, &event); + mstate &= ~DC_MOUSE_RIGHT; + } + } + else if ((caddr = maple_enum_type(0, MAPLE_FUNC_CONTROLLER)) && + (cont = maple_dev_status(caddr))) { + /* Enable mouse support */ + drv->capabilities |= GFX_CAPABILITY_MOUSE_SUPPORT; + + /* Semaphore prevents get_event() from accessing + ** S->pointer_dx/dy while they are being updated + */ + sem_wait(S->sem_pointer); + S->pointer_dx += cont->joyx/20; + S->pointer_dy += cont->joyy/20; + sem_signal(S->sem_pointer); + + if ((cont->ltrig > 5) && + !(mstate & DC_MOUSE_LEFT)) { + event.type = SCI_EVT_MOUSE_PRESS; + event.data = 1; + event.buckybits = bucky | vkbd_bucky; + dc_add_event(drv, &event); + mstate |= DC_MOUSE_LEFT; + } + if ((cont->rtrig > 5) && + !(mstate & DC_MOUSE_RIGHT)) { + event.type = SCI_EVT_MOUSE_PRESS; + event.data = 2; + event.buckybits = bucky | vkbd_bucky; + dc_add_event(drv, &event); + mstate |= DC_MOUSE_RIGHT; + } + if ((cont->ltrig <= 5) && + (mstate & DC_MOUSE_LEFT)) { + event.type = SCI_EVT_MOUSE_RELEASE; + event.data = 1; + event.buckybits = bucky | vkbd_bucky; + dc_add_event(drv, &event); + mstate &= ~DC_MOUSE_LEFT; + } + if ((cont->rtrig <= 5) && + (mstate & DC_MOUSE_RIGHT)) { + event.type = SCI_EVT_MOUSE_RELEASE; + event.data = 2; + event.buckybits = bucky | vkbd_bucky; + dc_add_event(drv, &event); + mstate &= ~DC_MOUSE_RIGHT; + } + if (S->timer > 0) + S->timer--; + if ((cont->buttons != S->cont_state) || !S->timer) { + S->cont_state = cont->buttons; + S->timer = 25; + if (cont->buttons & CONT_START) { + S->vkbd = S->vkbd ^ 1; + if (!(flags & SCI_DC_RENDER_PVR)) { + if(S->vkbd) + vram_show_keyboard(drv); + else + vram_hide_keyboard(drv); + } + } + if (S->vkbd) { + if (cont->buttons & CONT_DPAD_RIGHT) + vkbd_handle_input(KBD_RIGHT); + if (cont->buttons & CONT_DPAD_LEFT) + vkbd_handle_input(KBD_LEFT); + if (cont->buttons & CONT_DPAD_UP) + vkbd_handle_input(KBD_UP); + if (cont->buttons & CONT_DPAD_DOWN) + vkbd_handle_input(KBD_DOWN); + if (cont->buttons & CONT_A) { + int vkbd_key; + if (vkbd_get_key(&vkbd_key, &vkbd_bucky)) { + event.type = SCI_EVT_KEYBOARD; + event.data = vkbd_key; + event.buckybits = bucky | vkbd_bucky; + dc_add_event(drv, &event); + } + } + } + else { + event.data = 0; + if (cont->buttons & CONT_DPAD_RIGHT) + event.data = SCI_K_RIGHT; + else if (cont->buttons & CONT_DPAD_LEFT) + event.data = SCI_K_LEFT; + else if (cont->buttons & CONT_DPAD_UP) + event.data = SCI_K_UP; + else if (cont->buttons & CONT_DPAD_DOWN) + event.data = SCI_K_DOWN; + event.type = SCI_EVT_KEYBOARD; + event.buckybits = bucky | vkbd_bucky; + if (event.data) dc_add_event(drv, &event); + } + event.data = 0; + if (cont->buttons & CONT_B) + event.data = SCI_K_ENTER; + else if (cont->buttons & CONT_X) + event.data = ' '; + else if (cont->buttons & CONT_Y) + event.data = SCI_K_ESC; + event.type = SCI_EVT_KEYBOARD; + event.buckybits = bucky | vkbd_bucky; + if (event.data) + dc_add_event(drv, &event); + } + } + else drv->capabilities &= ~GFX_CAPABILITY_MOUSE_SUPPORT; + + S->buckystate = bucky | vkbd_bucky; + } +} + +static uint32 +dc_get_color(struct _gfx_driver *drv, gfx_color_t col) +/* Converts a color as described in col to it's representation in memory +** Parameters: (_gfx_driver *) drv: The driver to use +** (gfx_color_t) color: The color to convert +** Returns : (uint32) the color's representation in memory +*/ +{ + uint32 retval; + uint32 temp; + + retval = 0; + + temp = col.visual.r; + temp |= temp << 8; + temp |= temp << 16; + retval |= (temp >> drv->mode->red_shift) & (drv->mode->red_mask); + temp = col.visual.g; + temp |= temp << 8; + temp |= temp << 16; + retval |= (temp >> drv->mode->green_shift) & (drv->mode->green_mask); + temp = col.visual.b; + temp |= temp << 8; + temp |= temp << 16; + retval |= (temp >> drv->mode->blue_shift) & (drv->mode->blue_mask); + + return retval; +} + +static void +dc_draw_line_buffer(byte *buf, int line, int bytespp, int x1, + int y1, int x2, int y2, uint32 color) +/* Draws a line in a buffer +** This function was taken from sdl_driver.c with small modifications +** Parameters: (byte *) buf: The buffer to draw in +** (int) line: line pitch of buf in bytes +** (int) bytespp: number of bytes per pixel of buf +** (int) x1, y1, x2, y2: The line to draw: (x1,y1)-(x2,y2). +** (uint32) color: The color to draw with +** Returns : (void) +*/ +{ + int pixx, pixy; + int x,y; + int dx,dy; + int sx,sy; + int swaptmp; + uint8 *pixel; + + dx = x2 - x1; + dy = y2 - y1; + sx = (dx >= 0) ? 1 : -1; + sy = (dy >= 0) ? 1 : -1; + + dx = sx * dx + 1; + dy = sy * dy + 1; + pixx = bytespp; + pixy = line; + pixel = ((uint8*) buf) + pixx * x1 + pixy * y1; + pixx *= sx; + pixy *= sy; + if (dx < dy) { + swaptmp = dx; dx = dy; dy = swaptmp; + swaptmp = pixx; pixx = pixy; pixy = swaptmp; + } + + x=0; + y=0; + switch(bytespp) { + case 1: + for(; x < dx; x++, pixel += pixx) { + *pixel = color; + y += dy; + if (y >= dx) { + y -= dx; + pixel += pixy; + } + } + break; + case 2: + for (; x < dx; x++, pixel += pixx) { + *(uint16*)pixel = color; + y += dy; + if (y >= dx) { + y -= dx; + pixel += pixy; + } + } + break; + case 4: + for(; x < dx; x++, pixel += pixx) { + *(uint32*)pixel = color; + y += dy; + if (y >= dx) { + y -= dx; + pixel += pixy; + } + } + break; + } + +} + +static void +dc_draw_filled_rect_buffer(byte *buf, int line, int bytespp, rect_t rect, + uint32 color) +/* Draws a filled rectangle in a buffer +** Parameters: (byte *) buf: The buffer to draw in +** (int) line: line pitch of buf in bytes +** (int) bytespp: number of bytes per pixel of buf +** (rect_t) rect: The rectangle to fill +** (uint32) color: The fill color +** Returns : (void) +*/ +{ + buf += rect.y*line + rect.x*bytespp; + int i; + + switch (bytespp) { + case 1: for (i = 0; i<rect.yl; i++) { + memset(buf, color, rect.xl); + buf += line; + } + break; + case 2: for (i = 0; i<rect.yl; i++) { + memset2(buf, color, rect.xl*2); + buf += line; + } + break; + case 4: for (i = 0; i<rect.yl; i++) { + memset4(buf, color, rect.xl*4); + buf += line; + } + } +} + + +static void +dc_copy_rect_buffer(byte *src, byte *dest, int srcline, int destline, + int bytespp, rect_t sr, point_t dp) +/* Copies a rectangle from one buffer to another +** Parameters: (byte *) src: The source buffer +** (byte *) dest: The destination buffer +** (int) srcline: line pitch of src in bytes +** (int) destline: line pitch of dest in bytes +** (int) bytespp: number of bytes per pixel of src and dest +** (rect_t) sr: Rectangle of src to copy +** (point_t) dp: Left top corner in dest where copy should go +** Returns : (void) +*/ +{ + src += sr.y*srcline + sr.x*bytespp; + dest += dp.y*destline + dp.x*bytespp; + int i; + + switch (bytespp) { + case 1: for (i = 0; i<sr.yl; i++) { + memcpy(dest, src, sr.xl); + src += srcline; + dest += destline; + } + break; + case 2: for (i = 0; i<sr.yl; i++) { + memcpy2(dest, src, sr.xl*2); + src += srcline; + dest += destline; + } + break; + case 4: for (i = 0; i<sr.yl; i++) { + memcpy4(dest, src, sr.xl*4); + src += srcline; + dest += destline; + } + } +} + +static int +dc_set_parameter(struct _gfx_driver *drv, char *attribute, char *value) +{ + if (!strcasecmp(attribute, "render_mode")) { + if (!strcasecmp(value, "vram")) { + flags &= ~SCI_DC_RENDER_PVR; + return GFX_OK; + } + else if (!strcasecmp(value, "pvr")) { + flags |= SCI_DC_RENDER_PVR; + return GFX_OK; + } + else { + sciprintf("Fatal error: Invalid value `%s' specified for attribute `render_mode'\n", value); + return GFX_FATAL; + } + } + if (!strcasecmp(attribute, "refresh_rate")) { + if (!strcasecmp(value, "60Hz")) { + flags &= ~SCI_DC_REFRESH_50HZ; + return GFX_OK; + } + else if (!strcasecmp(value, "50Hz")) { + flags |= SCI_DC_REFRESH_50HZ; + return GFX_OK; + } + else { + sciprintf("Fatal error: Invalid value `%s' specified for attribute `refresh_rate'\n", value); + return GFX_FATAL; + } + } + + sciprintf("Fatal error: Attribute '%s' does not exist\n", attribute); + return GFX_FATAL; +} + + +static int +dc_init_specific(struct _gfx_driver *drv, int xfact, int yfact, int bytespp) +{ + int i; + int rmask = 0, gmask = 0, bmask = 0, rshift = 0, gshift = 0; + int bshift = 0; + + sciprintf("Initialising video mode\n"); + + pvr_shutdown(); + + if (!drv->state /* = S */) + drv->state = sci_malloc(sizeof(struct _dc_state)); + if (!S) + return GFX_FATAL; + + if ((flags & SCI_DC_RENDER_PVR) && ((xfact != 1 && xfact != 2) + || bytespp != 2 || xfact != yfact)) { + sciprintf("Error: PVR rendering mode does not support " + "buffers with scale factors (%d,%d) and bpp=%d\n", + xfact, yfact, bytespp); + return GFX_ERROR; + } + else if ((xfact != 1 && xfact != 2) || (bytespp != 2 && bytespp != 4) + || xfact != yfact) { + sciprintf("Error: VRAM rendering mode does not support " + "buffers with scale factors (%d,%d) and bpp=%d\n", + xfact, yfact, bytespp); + return GFX_ERROR; + } + + for (i = 0; i < 2; i++) { + if (!(S->priority[i] = sci_malloc(320*xfact*200*yfact)) || + !(S->visual[i] = sci_malloc(320*xfact*200*yfact* bytespp))) { + sciprintf("Error: Could not reserve memory for buffer\n"); + return GFX_ERROR; + } + } + + for (i = 0; i < 2; i++) { + S->line_pitch[i] = 320*xfact*bytespp; + memset(S->visual[i], 0, 320*xfact*200*yfact*bytespp); + memset(S->priority[i], 0, 320*xfact*200*yfact); + } + + S->pointer_dx = 0; + S->pointer_dy = 0; + S->buckystate = 0; + S->timer = 0; + S->vkbd = 0; + + switch(bytespp) { + case 2: rmask = 0xF800; + gmask = 0x7E0; + bmask = 0x1F; + rshift = 16; + gshift = 21; + bshift = 27; + break; + case 4: rmask = 0xFF0000; + gmask = 0xFF00; + bmask = 0xFF; + rshift = 8; + gshift = 16; + bshift = 24; + } + + if (!(flags & SCI_DC_RENDER_PVR)) + vram_init_gfx(drv, xfact, yfact, bytespp); + else + pvr_init_gfx(drv, xfact, yfact, bytespp); + + drv->mode = gfx_new_mode(xfact, yfact, bytespp, rmask, gmask, bmask, 0, + rshift, gshift, bshift, 0, 0, 0); + + printf("Video mode initialisation completed succesfully\n"); + + S->run_thread = 1; + + S->thread = thd_create((void *) dc_input_thread, drv); + + S->first_event = NULL; + S->last_event = NULL; + + if (!(S->sem_event = sem_create(1)) || + !(S->sem_pointer = sem_create(1))) { + printf("Error: Could not reserve memory for semaphore\n"); + return GFX_ERROR; + }; + + return GFX_OK; +} + +static int +dc_init(struct _gfx_driver *drv) +{ + if (dc_init_specific(drv, 1, 1, 2) != GFX_OK) + return GFX_FATAL; + + return GFX_OK; +} + +static void +dc_exit(struct _gfx_driver *drv) +{ + if (S) { + sciprintf("Freeing graphics buffers\n"); + sci_free(S->visual[0]); + sci_free(S->visual[1]); + sci_free(S->priority[0]); + sci_free(S->priority[1]); + if (flags & SCI_DC_RENDER_PVR) { + pvr_mem_free(S->visual[2]); + pvr_mem_free(S->vkbd_txr); + } + + sciprintf("Waiting for input thread to exit... "); + S->run_thread = 0; + thd_wait(S->thread); + sciprintf("ok\n"); + + sciprintf("Freeing semaphores\n"); + sem_destroy(S->sem_event); + sem_destroy(S->sem_pointer); + sci_free(S); + drv->state /* = S */ = NULL; + } +} + + /*** Drawing operations ***/ + +static int +dc_draw_line(struct _gfx_driver *drv, point_t start, point_t end, + gfx_color_t color, gfx_line_mode_t line_mode, + gfx_line_style_t line_style) +{ + uint32 scolor; + int xfact = (line_mode == GFX_LINE_MODE_FINE)? 1: XFACT; + int yfact = (line_mode == GFX_LINE_MODE_FINE)? 1: YFACT; + int xsize = XFACT*320; + int ysize = YFACT*200; + + int xc, yc; + int x1, y1, x2, y2; + + scolor = dc_get_color(drv, color); + + for (xc = 0; xc < xfact; xc++) + for (yc = 0; yc < yfact; yc++) { + x1 = start.x + xc; + y1 = start.y + yc; + x2 = end.x + xc; + y2 = end.y + yc; + + if (x1 < 0) + x1 = 0; + if (x2 < 0) + x2 = 0; + if (y1 < 0) + y1 = 0; + if (y2 < 0) + y2 = 0; + + if (x1 > xsize) + x1 = xsize; + if (x2 >= xsize) + x2 = xsize - 1; + if (y1 > ysize) + y1 = ysize; + if (y2 >= ysize) + y2 = ysize - 1; + + if (color.mask & GFX_MASK_VISUAL) + dc_draw_line_buffer(S->visual[1], + XFACT*320*BYTESPP, BYTESPP, x1, y1, x2, y2, + dc_get_color(drv, color)); + + if (color.mask & GFX_MASK_PRIORITY) + dc_draw_line_buffer(S->priority[1], XFACT*320, + 1, x1, y1, x2, y2, color.priority); + } + + return GFX_OK; +} + +static int +dc_draw_filled_rect(struct _gfx_driver *drv, rect_t rect, + gfx_color_t color1, gfx_color_t color2, gfx_rectangle_fill_t shade_mode) +{ + if (color1.mask & GFX_MASK_VISUAL) + dc_draw_filled_rect_buffer(S->visual[1], S->line_pitch[1], + BYTESPP, rect, dc_get_color(drv, color1)); + + if (color1.mask & GFX_MASK_PRIORITY) + dc_draw_filled_rect_buffer(S->priority[1], XFACT*320, 1, rect, + color1.priority); + + return GFX_OK; +} + + /*** Pixmap operations ***/ + +static int +dc_register_pixmap(struct _gfx_driver *drv, gfx_pixmap_t *pxm) +{ + return GFX_ERROR; +} + +static int +dc_unregister_pixmap(struct _gfx_driver *drv, gfx_pixmap_t *pxm) +{ + return GFX_ERROR; +} + +static int +dc_draw_pixmap(struct _gfx_driver *drv, gfx_pixmap_t *pxm, int priority, + rect_t src, rect_t dest, gfx_buffer_t buffer) +{ + int bufnr = (buffer == GFX_BUFFER_STATIC)? 0:1; + + return gfx_crossblit_pixmap(drv->mode, pxm, priority, src, dest, + S->visual[bufnr], S->line_pitch[bufnr], S->priority[bufnr], XFACT*320, + 1, 0); +} + +static int +dc_grab_pixmap(struct _gfx_driver *drv, rect_t src, gfx_pixmap_t *pxm, + gfx_map_mask_t map) +{ + switch (map) { + case GFX_MASK_VISUAL: + dc_copy_rect_buffer(S->visual[1], pxm->data, + S->line_pitch[1], src.xl*BYTESPP, BYTESPP, src, + gfx_point(0, 0)); + pxm->xl = src.xl; + pxm->yl = src.yl; + return GFX_OK; + case GFX_MASK_PRIORITY: + dc_copy_rect_buffer(S->priority[1], pxm->index_data, + XFACT*320, src.xl, 1, src, gfx_point(0, 0)); + pxm->index_xl = src.xl; + pxm->index_yl = src.yl; + return GFX_OK; + default: + sciprintf("Error: attempt to grab pixmap from invalid map"); + return GFX_ERROR; + } +} + + + /*** Buffer operations ***/ + +static int +dc_update(struct _gfx_driver *drv, rect_t src, point_t dest, gfx_buffer_t buffer) +{ + int tbufnr = (buffer == GFX_BUFFER_BACK)? 1:2; + + dc_copy_rect_buffer(S->visual[tbufnr-1], S->visual[tbufnr], + S->line_pitch[tbufnr-1], S->line_pitch[tbufnr], BYTESPP, src, dest); + + if ((tbufnr == 1) && (src.x == dest.x) && (src.y == dest.y)) + dc_copy_rect_buffer(S->priority[0], S->priority[1], XFACT*320, + XFACT*320, 1, src, dest); + + return GFX_OK; +} + +static int +dc_set_static_buffer(struct _gfx_driver *drv, gfx_pixmap_t *pic, gfx_pixmap_t *priority) +{ + memcpy4(S->visual[0], pic->data, XFACT*320 * YFACT*200 * BYTESPP); + memcpy4(S->priority[0], priority->index_data, XFACT*320 * YFACT*200); + return GFX_OK; +} + + /*** Palette operations ***/ + +static int +dc_set_palette(struct _gfx_driver *drv, int index, byte red, byte green, byte blue) +{ + return GFX_ERROR; +} + + + /*** Mouse pointer operations ***/ + +static int +dc_set_pointer (struct _gfx_driver *drv, gfx_pixmap_t *pointer) +{ + return GFX_ERROR; +} + + /*** Event management ***/ + +static sci_event_t +dc_get_event(struct _gfx_driver *drv) +{ + sci_event_t event; + struct dc_event_t *first; + sem_wait(S->sem_pointer); + drv->pointer_x += S->pointer_dx; + drv->pointer_y += S->pointer_dy; + S->pointer_dx = 0; + S->pointer_dy = 0; + sem_signal(S->sem_pointer); + + if (drv->pointer_x < 0) + drv->pointer_x = 0; + if (drv->pointer_x >= 320*XFACT) + drv->pointer_x = 320*XFACT-1; + if (drv->pointer_y < 0) + drv->pointer_y = 0; + if (drv->pointer_y >= 200*YFACT) + drv->pointer_y = 200*YFACT-1; + + sem_wait(S->sem_event); + first = S->first_event; + + if (first) { + event = first->event; + S->first_event = first->next; + free(first); + if (S->first_event == NULL) S->last_event = NULL; + sem_signal(S->sem_event); + return event; + } + + sem_signal(S->sem_event); + event.type = SCI_EVT_NONE; + event.buckybits = S->buckystate; + return event; +} + + +static int +dc_usec_sleep(struct _gfx_driver *drv, long usecs) +{ + /* TODO: wake up on mouse move */ + int ms = usecs/1000; + if (ms) + thd_sleep(ms); + return GFX_OK; +} + +gfx_driver_t +gfx_driver_dc = { + "dc", + "0.2b", + SCI_GFX_DRIVER_MAGIC, + SCI_GFX_DRIVER_VERSION, + NULL, + 0, + 0, + GFX_CAPABILITY_FINE_LINES, + 0, + dc_set_parameter, + dc_init_specific, + dc_init, + dc_exit, + dc_draw_line, + dc_draw_filled_rect, + dc_register_pixmap, + dc_unregister_pixmap, + dc_draw_pixmap, + dc_grab_pixmap, + dc_update, + dc_set_static_buffer, + dc_set_pointer, + dc_set_palette, + dc_get_event, + dc_usec_sleep, + NULL +}; diff --git a/engines/sci/gfx/drivers/dd_driver.cpp b/engines/sci/gfx/drivers/dd_driver.cpp new file mode 100644 index 0000000000..f2920b4089 --- /dev/null +++ b/engines/sci/gfx/drivers/dd_driver.cpp @@ -0,0 +1,1021 @@ +#ifndef __cplusplus +#error NOTE: This file MUST be compiled as C++. In Visual C++, use the /Tp command line option. +#endif + +// for WINNT 4.0 (only DirectDraw 3) +#ifdef HAVE_DDRAW + +#define DIRECTDRAW_VERSION 0x0300 + +#define INITGUID + +#include <windows.h> +#include <ddraw.h> +#include <stdarg.h> + +extern "C" { +#include <gfx_system.h> +#include <gfx_driver.h> +#include <gfx_tools.h> +#include <assert.h> +#include <uinput.h> +#include <ctype.h> +#include <console.h> // for sciprintf +#include <sci_memory.h> +}; + +#include "dd_driver.h" + +#define DD_DRIVER_VERSION "0.1" + +#define GFX_DD_DEBUG + + +#ifdef GFX_DD_DEBUG +#define POSMSG sciprintf("%s L%d:", __FILE__, __LINE__) +#define DEBUG_PTR (!(drv->debug_flags & GFX_DEBUG_POINTER))? 0 : POSMSG && sciprintf +#define DEBUG_UPDATES (!(drv->debug_flags & GFX_DEBUG_UPDATES))? 0 : POSMSG && sciprintf +#define DEBUG_PIXMAPS (!(drv->debug_flags & GFX_DEBUG_PIXMAPS))? 0 : POSMSG && sciprintf +#define DEBUG_BASIC (!(drv->debug_flags & GFX_DEBUG_BASIC))? 0 : POSMSG , sciprintf +#else /* !GFX_DD_DEBUG */ +#define DEBUG_PTR (1)? 0 : +#define DEBUG_UPDATES (1)? 0 : +#define DEBUG_PIXMAPS (1)? 0 : +#define DEBUG_BASIC (1)? 0 : +#endif /* !GFX_DD_DEBUG */ + +BOOL g_bFullScreen = FALSE; + +#define DD_BUFFER_BACK 0 +#define DD_BUFFER_STATIC 1 + +static long FAR PASCAL WndProc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); +static int process_messages(void); + +struct gfx_dd_struct_t +{ + LPDIRECTDRAW pDD; + HWND hMainWnd; + LPDIRECTDRAWSURFACE pPrimary; + LPDIRECTDRAWSURFACE pBack; + LPDIRECTDRAWSURFACE pStatic; + LPDIRECTDRAWCLIPPER pClipper; // used in windowed (not fullscreen) mode + int win_xpos,win_ypos; // current position of the window (windowed mode only) + BOOL bShowMouse; + gfx_pixmap_t *priority_maps[2]; + // event queue + int queue_size,queue_first,queue_last; + sci_event_t * event_queue; +}; + +static void init_event_queue(gfx_dd_struct_t *ctx); +static void free_event_queue(gfx_dd_struct_t *ctx); + +static __inline DWORD MakeRGB(gfx_mode_t *mode,unsigned int r,unsigned int g,unsigned int b) +{ + return ((r << mode->red_shift) & mode->red_mask) | + ((g << mode->green_shift) & mode->green_mask) | + ((b << mode->blue_shift) & mode->blue_mask); +}; + +static int +dd_set_param(gfx_driver_t *drv, char *attribute, char *value) +{ + DEBUG_BASIC("dd_set_param('%s' to '%s')\n", attribute, value); + return GFX_OK; +} + +static int +ShiftCount(DWORD mask) +{ + int cnt; + + if(mask==0) + return 0; // error !!! + + cnt=0; + + while((mask & 1)==0) + { + mask >>=1; + cnt++; + } + return cnt; +} + +static int +dd_init_specific(gfx_driver_t *drv, int xres, int yres, int bpp) +{ + DDSURFACEDESC dd_desc,ddsd; + HRESULT hr; + WNDCLASS wc; + RECT rc; + POINT pnt; + gfx_dd_struct_t *ctx; + int dd_bpp,r_sh,g_sh,b_sh,dd_bpp2,tbytes; +// const ggi_pixelformat *pixelformat; + int frames = 3; + + // force .... + xres=1; yres=1; bpp=1; + + if(g_bFullScreen) + { + // force the best mode + xres=1; yres=1; bpp=1; + } + switch(bpp) // bpp of 8,16,24,32 + { + case 1: + dd_bpp=8; break; + case 2: + dd_bpp=16; break; + case 3: + dd_bpp=24; break; + case 4: + dd_bpp=32; break; + default: + sciprintf("GFXDD: Error: Invalid bytes per pixel value: %d\n", bpp); + return GFX_ERROR; + } + + drv->state = NULL; + + ctx = (struct gfx_dd_struct_t *) sci_malloc(sizeof(gfx_dd_struct_t)); + if(ctx == NULL) + return GFX_FATAL; + + memset(ctx,0,sizeof(gfx_dd_struct_t)); + + wc.style = CS_HREDRAW | CS_VREDRAW; + wc.lpfnWndProc = WndProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = sizeof(void *); + wc.hInstance = NULL; + wc.hIcon = LoadIcon (NULL, IDI_APPLICATION); + wc.hCursor = NULL; /*LoadCursor (NULL, IDC_ARROW)*/; + wc.hbrBackground = (HBRUSH) GetStockObject (BLACK_BRUSH); + wc.lpszMenuName = NULL; + wc.lpszClassName = "freesci.WndClass"; + RegisterClass (&wc); + + SetRect (&rc, 0, 0, xres*320-1, yres*200-1); + if(!g_bFullScreen) + { + AdjustWindowRectEx (&rc, WS_OVERLAPPEDWINDOW, FALSE, 0); + } + + init_event_queue(ctx); + + ctx->hMainWnd = CreateWindowEx (0,"freesci.WndClass","FreeSCI", + g_bFullScreen ? WS_POPUP : (WS_OVERLAPPED | WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU), + 0,0,rc.right-rc.left,rc.bottom-rc.top,NULL,NULL,NULL,NULL); + + SetWindowLong(ctx->hMainWnd,0,(long) drv); + + hr = DirectDrawCreate(NULL,&(ctx->pDD),NULL); + if ( FAILED(hr)) + { + DestroyWindow(ctx->hMainWnd); + free(ctx); + return GFX_FATAL; + } + + + if(g_bFullScreen) + { + // fulscreen mode, change to the desired mode + hr = ctx->pDD->SetCooperativeLevel ( ctx->hMainWnd, DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN); + hr = ctx->pDD->SetDisplayMode ( xres*320, yres*200, dd_bpp); + } + else + { + // windowed mode, accept whatever mode is current + hr = ctx->pDD->SetCooperativeLevel (ctx->hMainWnd, DDSCL_NORMAL); + } + + drv->state = ctx; + + ShowWindow(ctx->hMainWnd,SW_NORMAL); + UpdateWindow(ctx->hMainWnd); + + pnt.x = 0; pnt.y = 0; + ClientToScreen (ctx->hMainWnd, &pnt); + ctx->win_xpos = pnt.x; + ctx->win_ypos = pnt.y; + + // create surface - primary + memset(&dd_desc,0,sizeof(dd_desc)); + dd_desc.dwSize = sizeof(dd_desc); + dd_desc.dwFlags = DDSD_CAPS; + dd_desc.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE; + hr = ctx->pDD->CreateSurface(&dd_desc,&ctx->pPrimary,NULL); + if(FAILED(hr)) + { + if(g_bFullScreen) + { + hr = ctx->pDD->RestoreDisplayMode (); + hr = ctx->pDD->SetCooperativeLevel (ctx->hMainWnd, DDSCL_NORMAL); + } + ctx->pDD->Release(); + free(ctx); + return GFX_FATAL; + } + + // get the current mode; in windowed mode it can be different than the one we want + memset(&dd_desc,0,sizeof(dd_desc)); + dd_desc.dwSize = sizeof(dd_desc); + hr = ctx->pDD->GetDisplayMode(&dd_desc); + + r_sh = ShiftCount(dd_desc.ddpfPixelFormat.dwRBitMask); + g_sh = ShiftCount(dd_desc.ddpfPixelFormat.dwGBitMask); + b_sh = ShiftCount(dd_desc.ddpfPixelFormat.dwBBitMask); + dd_bpp2=0; + if(dd_desc.ddpfPixelFormat.dwFlags & DDPF_PALETTEINDEXED1) + dd_bpp2=2; + if(dd_desc.ddpfPixelFormat.dwFlags & DDPF_PALETTEINDEXED2) + dd_bpp2=4; + if(dd_desc.ddpfPixelFormat.dwFlags & DDPF_PALETTEINDEXED4) + dd_bpp2=16; + if(dd_desc.ddpfPixelFormat.dwFlags & DDPF_PALETTEINDEXED8) + dd_bpp2=256; + + drv->mode = gfx_new_mode(xres,yres,dd_desc.ddpfPixelFormat.dwRGBBitCount/8, + dd_desc.ddpfPixelFormat.dwRBitMask,dd_desc.ddpfPixelFormat.dwGBitMask,dd_desc.ddpfPixelFormat.dwBBitMask,0, + r_sh,g_sh,b_sh,0, dd_bpp2, 0); + + tbytes = 320*xres*200*yres* (dd_desc.ddpfPixelFormat.dwRGBBitCount/8); + + // create a secondary surfaces + memset (&ddsd, 0, sizeof (DDSURFACEDESC)); + ddsd.dwSize = sizeof (DDSURFACEDESC); + ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT; + ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN; + ddsd.dwWidth = 320 * drv->mode->xfact; + ddsd.dwHeight = 200 * drv->mode->yfact; + hr = ctx->pDD->CreateSurface(&ddsd,&ctx->pBack,NULL); + if(FAILED(hr)) + { + return GFX_FATAL; + } + memset (&ddsd, 0, sizeof (DDSURFACEDESC)); + ddsd.dwSize = sizeof (DDSURFACEDESC); + hr = ctx->pBack->Lock(NULL,&ddsd,DDLOCK_WAIT,NULL); + if(!FAILED(hr)) + { + memset(ddsd.lpSurface,0,tbytes); + hr = ctx->pBack->Unlock(NULL); + } + + memset (&ddsd, 0, sizeof (DDSURFACEDESC)); + ddsd.dwSize = sizeof (DDSURFACEDESC); + ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT; + ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN; + ddsd.dwWidth = 320 * drv->mode->xfact; + ddsd.dwHeight = 200 * drv->mode->yfact; + hr = ctx->pDD->CreateSurface(&ddsd,&ctx->pStatic,NULL); + if(FAILED(hr)) + { + return GFX_FATAL; + } + memset (&ddsd, 0, sizeof (DDSURFACEDESC)); + ddsd.dwSize = sizeof (DDSURFACEDESC); + hr = ctx->pStatic->Lock(NULL,&ddsd,DDLOCK_WAIT,NULL); + if(!FAILED(hr)) + { + memset(ddsd.lpSurface,0,tbytes); + hr = ctx->pStatic->Unlock(NULL); + } + + // if not in full-screen mode, set the clipper + if (!g_bFullScreen) + { + hr = ctx->pDD->CreateClipper (0, &ctx->pClipper, NULL); + hr = ctx->pClipper->SetHWnd (0, ctx->hMainWnd); + hr = ctx->pPrimary->SetClipper (ctx->pClipper); + } + + ctx->priority_maps[DD_BUFFER_BACK] = + gfx_pixmap_alloc_index_data(gfx_new_pixmap(320 * xres, 200 * yres, GFX_RESID_NONE, 0, 0)); + ctx->priority_maps[DD_BUFFER_STATIC] = + gfx_pixmap_alloc_index_data(gfx_new_pixmap(320 * xres, 200 * yres, GFX_RESID_NONE, 0, 0)); + + ctx->priority_maps[DD_BUFFER_BACK]->flags |= GFX_PIXMAP_FLAG_SCALED_INDEX; + ctx->priority_maps[DD_BUFFER_STATIC]->flags |= GFX_PIXMAP_FLAG_SCALED_INDEX; + +/* + if (_open_meta_visuals(drv)) { + free(meta); + gfx_free_pixmap(drv, meta->priority_maps[GGI_BUFFER_BACK]); + gfx_free_pixmap(drv, meta->priority_maps[GGI_BUFFER_STATIC]); + ggiClose(meta->vis); + ggiExit(); + return GFX_ERROR; + } + + if (frames < 2) { + meta->alt_back_buffer = sci_malloc(bpp * 320 * 200 * xres * yres); + meta->back_vis = ggiOpen("memory:pointer", meta->alt_back_buffer, NULL); + if (ggiSetSimpleMode(meta->back_vis, xres * 320, yres * 200, 1, GT_8BIT)) { + sciprintf("GFXGGI: Warning: Setting mode for memory visual failed\n"); + } + } else meta->alt_back_buffer = NULL; + + if (frames < 3) { + meta->static_buffer = sci_malloc(bpp * 320 * 200 * xres * yres); + meta->static_vis = ggiOpen("memory:pointer", meta->static_buffer, NULL); + if (ggiSetSimpleMode(meta->static_vis, xres * 320, yres * 200, 1, GT_8BIT)) { + sciprintf("GFXGGI: Warning: Setting mode for memory visual #2 failed\n"); + } + } else meta->static_buffer = NULL; + + init_input_ggi(); +*/ + return GFX_OK; +} + +int +dd_init(gfx_driver_t *drv) +{ + return dd_init_specific(drv,1,1,1); +} + +void +dd_exit(gfx_driver_t *drv) +{ + HRESULT hr; + gfx_dd_struct_t *ctx; + + if(drv->state == NULL) + return; + + ctx = (gfx_dd_struct_t *) drv->state; + + if(g_bFullScreen) + { + hr = ctx->pDD->RestoreDisplayMode (); + hr = ctx->pDD->SetCooperativeLevel (ctx->hMainWnd, DDSCL_NORMAL); + } + + free_event_queue(ctx); + + gfx_free_pixmap(drv, ctx->priority_maps[0]); + gfx_free_pixmap(drv, ctx->priority_maps[1]); + + if(ctx->hMainWnd) + { + DestroyWindow(ctx->hMainWnd); + ctx->hMainWnd = NULL; + } + + if(ctx->pBack) + { + ctx->pBack->Release(); + ctx->pBack=NULL; + } + if(ctx->pStatic) + { + ctx->pStatic->Release(); + ctx->pStatic=NULL; + } + if(ctx->pPrimary) + { + ctx->pPrimary->Release(); + ctx->pPrimary=NULL; + } + if(ctx->pClipper) + { + ctx->pClipper->Release(); + ctx->pClipper=NULL; + } + if(ctx->pDD) + { + ctx->pDD->Release(); + ctx->pDD = NULL; + } + + drv->state = NULL; + + free(ctx); +} + +int +dd_draw_line(gfx_driver_t *drv, + point_t start, point_t end, + gfx_color_t color, + gfx_line_mode_t line_mode, gfx_line_style_t line_style) +{ + rect_t line = gfx_rect(start.x, start.y, + end.x - start.x, end.y - start.y); + HRESULT hr; + DDSURFACEDESC ddsc; + gfx_dd_struct_t *ctx; + + if(drv->state == NULL) + return GFX_ERROR; + + ctx = (gfx_dd_struct_t *) drv->state; + + memset(&ddsc,0,sizeof(ddsc)); + ddsc.dwSize = sizeof(ddsc); + hr = ctx->pBack->Lock(NULL,&ddsc,DDLOCK_WAIT,NULL); + if(FAILED(hr)) + { + return GFX_ERROR; + } + + _dd_draw_line(drv->mode, start, end, (byte *) ddsc.lpSurface, ddsc.lPitch,color); + +// gfx_crossblit_pixmap(drv->mode, pxm, priority, src, dest, (byte *) ddsc.lpSurface, +// ddsc.lPitch,pri_map, drv->mode->xfact * 320, 1); + + hr = ctx->pBack->Unlock(NULL); + return GFX_OK; +} + +int +dd_draw_filled_rect(gfx_driver_t *drv, rect_t box, gfx_color_t color1, gfx_color_t color2, + gfx_rectangle_fill_t shade_mode) +{ + HRESULT hr; + RECT rcDest; + DDBLTFX ddblt; + gfx_dd_struct_t *ctx; + byte *pri; + int i; + + if(drv->state == NULL) + return GFX_ERROR; + + ctx = (gfx_dd_struct_t *) drv->state; + + rcDest.left = box.x; + rcDest.top = box.y; + rcDest.right = box.x+box.xl; + rcDest.bottom = box.y+box.yl; + + if (color1.mask & GFX_MASK_VISUAL) + { + memset(&ddblt,0,sizeof(ddblt)); + ddblt.dwSize = sizeof(ddblt); + if(drv->mode->palette!=NULL) + ddblt.dwFillColor = color1.visual.global_index; + else + ddblt.dwFillColor = MakeRGB(drv->mode,color1.visual.r,color1.visual.g,color1.visual.b); + hr = ctx->pBack->Blt(&rcDest,NULL,NULL,DDBLT_COLORFILL,&ddblt); + } + + if (color1.mask & GFX_MASK_PRIORITY) + { + pri = ctx->priority_maps[DD_BUFFER_BACK]->index_data + box.x + box.y*(drv->mode->xfact * 320); + for(i=0; i<box.yl; i++) + { + memset(pri,color1.priority,box.xl); + pri += drv->mode->xfact * 320; + } + } + + return GFX_OK; +} + +int +dd_draw_pixmap(gfx_driver_t *drv, gfx_pixmap_t *pxm, int priority, + rect_t src, rect_t dest, gfx_buffer_t buffer) +{ + HRESULT hr; + gfx_dd_struct_t *ctx; + LPDIRECTDRAWSURFACE dst; + DDSURFACEDESC ddsc; + byte *pri_map = NULL; + + if(drv->state == NULL) + return GFX_ERROR; + + ctx = (gfx_dd_struct_t *) drv->state; + + if (src.xl != dest.xl || src.yl != dest.yl) + { + GFXERROR("Attempt to draw scaled view- not supported by this driver!\n"); + return GFX_FATAL; /* Scaling not supported! */ + } + + switch(buffer) + { + case GFX_BUFFER_FRONT: + GFXERROR("Attempt to draw pixmap to front buffer\n"); + return GFX_ERROR; + case GFX_BUFFER_BACK: + dst = ctx->pBack; + pri_map = ctx->priority_maps[DD_BUFFER_BACK]->index_data; + break; + case GFX_BUFFER_STATIC: + dst = ctx->pStatic; + pri_map = ctx->priority_maps[DD_BUFFER_STATIC]->index_data; + break; + default: + GFXERROR("Unexpected buffer ID %d\n", buffer); + return GFX_ERROR; + } + + memset(&ddsc,0,sizeof(ddsc)); + ddsc.dwSize = sizeof(ddsc); + hr = dst->Lock(NULL,&ddsc,DDLOCK_WAIT,NULL); + if(FAILED(hr)) + { + return GFX_ERROR; + } + + gfx_crossblit_pixmap(drv->mode, pxm, priority, src, dest, (byte *) ddsc.lpSurface, + ddsc.lPitch,pri_map, drv->mode->xfact * 320, 1, 0); + + hr = dst->Unlock(NULL); + + return GFX_OK; +} + +int +dd_grab_pixmap(gfx_driver_t *drv, rect_t src, gfx_pixmap_t *pxm, gfx_map_mask_t map) +{ + HRESULT hr; + gfx_dd_struct_t *ctx; + DDSURFACEDESC ddsc; + int i,x,y,xlb,bpp; + BYTE *s,*d; + + if(drv->state == NULL) + return GFX_ERROR; + + ctx = (gfx_dd_struct_t *) drv->state; + + if (src.x < 0 || src.y < 0) + { + GFXERROR("Attempt to grab pixmap from invalid coordinates (%d,%d)\n", src.x, src.y); + return GFX_ERROR; + } + + if (!pxm->data) + { + GFXERROR("Attempt to grab pixmap to unallocated memory\n"); + return GFX_ERROR; + } + + pxm->xl = src.xl; + pxm->yl = src.yl; + + memset(&ddsc,0,sizeof(ddsc)); + ddsc.dwSize = sizeof(ddsc); + hr = ctx->pBack->Lock(NULL,&ddsc,DDLOCK_WAIT,NULL); + if(FAILED(hr)) + return GFX_ERROR; + + bpp = ddsc.ddpfPixelFormat.dwRGBBitCount / 8; + x = src.x*drv->mode->xfact; + y = src.y*drv->mode->yfact; +/* + if(!g_bFullScreen) + { + x += ctx->win_xpos; + y += ctx->win_ypos; + } +*/ + s = (BYTE *) ddsc.lpSurface + x*bpp + y*ddsc.lPitch; + d = pxm->data; + xlb = src.xl * bpp; + + for(i=0; i<src.yl; i++) + { + memcpy(d,s,xlb); + d += xlb; + s += ddsc.lPitch; + } + + hr = ctx->pBack->Unlock(NULL); + + return GFX_OK; +} + +static int +dd_update(gfx_driver_t *drv, rect_t src, point_t dest, gfx_buffer_t buffer) +{ + gfx_dd_struct_t *ctx; + RECT rcDst,rcSrc; + HRESULT hr; + LPDIRECTDRAWSURFACE p_dst,p_src; + + if(drv->state == NULL) + return GFX_ERROR; + + ctx = (gfx_dd_struct_t *) drv->state; + + rcSrc.left = src.x; + rcSrc.right = src.x + src.xl; + rcSrc.top = src.y; + rcSrc.bottom = src.y + src.yl; + + rcDst.left = dest.x; + rcDst.right = dest.x + src.xl; + rcDst.top = dest.y; + rcDst.bottom = dest.y + src.yl; + + switch(buffer) + { + case GFX_BUFFER_FRONT: + rcDst.left += ctx->win_xpos; + rcDst.right += ctx->win_xpos; + rcDst.top += ctx->win_ypos; + rcDst.bottom += ctx->win_ypos; + p_dst = ctx->pPrimary; + p_src = ctx->pBack; + break; + case GFX_BUFFER_BACK: + p_dst = ctx->pBack; + p_src = ctx->pStatic; + break; + default: + return GFX_ERROR; + } + + hr = p_dst->Blt(&rcDst,p_src,&rcSrc,DDBLT_WAIT,NULL); + if(FAILED(hr)) + return GFX_ERROR; + + return GFX_OK; +} + +static int +dd_set_static_buffer(gfx_driver_t *drv, gfx_pixmap_t *pic, gfx_pixmap_t *priority) +{ + gfx_dd_struct_t *ctx; + DDSURFACEDESC ddsc; + HRESULT hr; + int i,xs; + byte *s,*d; +// ggi_visual_t vis = get_writeable_static_visual(drv); + + if(drv->state == NULL) + return GFX_ERROR; + + ctx = (gfx_dd_struct_t *) drv->state; + + /* First, check if the priority map is sane */ + if (priority->index_xl != ctx->priority_maps[DD_BUFFER_STATIC]->index_xl + || priority->index_yl != ctx->priority_maps[DD_BUFFER_STATIC]->index_yl) + { + GFXERROR("Invalid priority map: (%dx%d) vs expected (%dx%d)\n", + priority->index_xl, priority->index_yl, + ctx->priority_maps[DD_BUFFER_STATIC]->index_xl, + ctx->priority_maps[DD_BUFFER_STATIC]->index_yl); + return GFX_ERROR; + } + +// ggiPutBox(vis, 0, 0, pic->xl, pic->yl, pic->data); + memset(&ddsc,0,sizeof(ddsc)); + ddsc.dwSize = sizeof(ddsc); + hr = ctx->pStatic->Lock(NULL,&ddsc,DDLOCK_WAIT,NULL); + if(FAILED(hr)) + { + return GFX_ERROR; + } + + s = pic->data; + d = (byte *) ddsc.lpSurface; + xs = drv->mode->bytespp * pic->xl; + + for(i=0; i<pic->yl; i++) + { + memcpy(d,s,xs); + s+= xs; + d+= ddsc.lPitch; + } + + hr = ctx->pStatic->Unlock(NULL); + + memcpy(ctx->priority_maps[DD_BUFFER_STATIC]->index_data, priority->index_data, + priority->index_xl * priority->index_yl); + + return GFX_OK; +} + +static int +dd_set_palette(gfx_driver_t *drv, int index, byte red, byte green, byte blue) +{ + //!! implement me !! + return GFX_ERROR; +} + +// ------------------------------------- non-drawing functions ------------------------------------ +sci_event_t get_queue_event(gfx_dd_struct_t *ctx); + +static int process_messages(void) +{ + MSG msg; + + while (PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE)) + { + if (!GetMessage (&msg, NULL, 0, 0)) + return 0; + TranslateMessage (&msg); + DispatchMessage(&msg); + } + return 1; +} + +static sci_event_t +dd_get_event(gfx_driver_t *drv) +{ + gfx_dd_struct_t *ctx; + + process_messages(); + + assert(drv->state!=NULL); + + ctx = (gfx_dd_struct_t *) drv->state; + + return get_queue_event(ctx); +} + +static void +MsgWait (long WaitTime) +{ + DWORD dwRet=0; + long dwWait; + DWORD StartTime=timeGetTime(); + + dwWait = WaitTime; + + if (!process_messages()) + return; + + if (dwWait <= 0) + return; + do + { + dwWait=WaitTime-(timeGetTime()-StartTime); + if (dwWait <= 0) + break; + + dwRet=MsgWaitForMultipleObjects (0, NULL, FALSE, dwWait, QS_ALLINPUT); + + if(dwRet == WAIT_TIMEOUT) + return; + + if (!process_messages()) + return; + + } while(1); +} + +int +dd_usleep(gfx_driver_t* drv, long usec) +{ + if (usec < 1000) + Sleep(usec / 1000); + else + MsgWait (usec / 1000); + + return GFX_OK; +} +/* --------------------------------------------------------------- */ + +static void init_event_queue(gfx_dd_struct_t *ctx) +{ + ctx->queue_first = 0; + ctx->queue_last = 0; + ctx->queue_size = 256; + ctx->event_queue = (sci_event_t *) sci_malloc (ctx->queue_size * sizeof (sci_event_t)); +} + +static void free_event_queue(gfx_dd_struct_t *ctx) +{ + if (ctx->event_queue) sci_free(ctx->event_queue); + ctx->queue_size = 0; + ctx->queue_first =0; + ctx->queue_last =0; +} + +static void add_queue_event(gfx_dd_struct_t *ctx,int type, int data, int buckybits) +{ + if ((ctx->queue_last+1) % ctx->queue_size == ctx->queue_first) + { + /* Reallocate queue */ + int i, event_count; + sci_event_t *new_queue; + + new_queue = (sci_event_t *) sci_malloc (ctx->queue_size * 2 * sizeof (sci_event_t)); + event_count = (ctx->queue_last - ctx->queue_first) % ctx->queue_size; + for (i=0; i<event_count; i++) + new_queue [i] = ctx->event_queue [(ctx->queue_first+i) % ctx->queue_size]; + free (ctx->event_queue); + ctx->event_queue = new_queue; + ctx->queue_size *= 2; + ctx->queue_first = 0; + ctx->queue_last = event_count; + } + + ctx->event_queue [ctx->queue_last].data = data; + ctx->event_queue [ctx->queue_last].type = type; + ctx->event_queue [ctx->queue_last++].buckybits = buckybits; + if (ctx->queue_last == ctx->queue_size) + ctx->queue_last=0; +} + +static void add_mouse_event (gfx_dd_struct_t *ctx,int type, int data, WPARAM wParam) +{ + int buckybits = 0; + + if (wParam & MK_SHIFT) + buckybits |= SCI_EVM_LSHIFT | SCI_EVM_RSHIFT; + if (wParam & MK_CONTROL) + buckybits |= SCI_EVM_CTRL; + + add_queue_event (ctx,type, data, buckybits); +} + +static void add_key_event (gfx_dd_struct_t *ctx,int data) +{ + int buckybits = 0; + + /* FIXME: If anyone cares, on Windows NT we can distinguish left and right shift */ + if (GetAsyncKeyState (VK_SHIFT)) + buckybits |= SCI_EVM_LSHIFT | SCI_EVM_RSHIFT; + if (GetAsyncKeyState (VK_CONTROL)) + buckybits |= SCI_EVM_CTRL; + if (GetAsyncKeyState (VK_MENU)) + buckybits |= SCI_EVM_ALT; + if (GetKeyState (VK_CAPITAL) & 1) + buckybits |= SCI_EVM_CAPSLOCK; + + add_queue_event (ctx,SCI_EVT_KEYBOARD, data, buckybits); +} + +sci_event_t get_queue_event(gfx_dd_struct_t *ctx) +{ + if (ctx->queue_first == ctx->queue_size) + ctx->queue_first = 0; + + if (ctx->queue_first == ctx->queue_last) + { + sci_event_t noevent; + noevent.data = 0; + noevent.type = SCI_EVT_NONE; + noevent.buckybits = 0; + return noevent; + } + else + return ctx->event_queue [ctx->queue_first++]; +} + +#define MAP_KEY(x,y) case x: add_key_event (ctx, y); break + +static long FAR PASCAL WndProc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + HRESULT hr; + gfx_dd_struct_t *ctx; + gfx_driver_t* drv; + + drv = (gfx_driver_t *) GetWindowLong(hWnd,0); + if(drv == NULL) + return DefWindowProc(hWnd,message,wParam,lParam); + ctx = (gfx_dd_struct_t *) drv->state; + if(ctx == NULL) + return DefWindowProc(hWnd,message,wParam,lParam); + + switch(message) + { + case WM_SIZE: + case WM_MOVE: + { + POINT pnt; + pnt.x = 0; pnt.y = 0; + ClientToScreen (hWnd, &pnt); + ctx->win_xpos = pnt.x; + ctx->win_ypos = pnt.y; + } + break; + case WM_PAINT: + { + RECT rcSrc,rcDst; + if (ctx->pPrimary!=NULL && ctx->pBack!=NULL && GetUpdateRect (hWnd, &rcSrc, FALSE)) + { + if(rcSrc.right>320) + rcSrc.right=320; + if(rcSrc.bottom>200) + rcSrc.bottom=200; + rcDst.left = rcSrc.left + ctx->win_xpos; + rcDst.right = rcSrc.right + ctx->win_xpos; + rcDst.top = rcSrc.top + ctx->win_ypos; + rcDst.bottom = rcSrc.bottom + ctx->win_ypos; + hr = ctx->pPrimary->Blt(&rcDst,ctx->pBack,&rcSrc,DDBLT_WAIT,NULL); + ValidateRect(hWnd,&rcSrc); + } + } + break; + case WM_SETCURSOR: + if(IsIconic(hWnd) || ctx->bShowMouse) + { + SetCursor(LoadCursor (NULL, IDC_ARROW)); + } + else + { + SetCursor(NULL); + } + return TRUE; + case WM_NCMOUSEMOVE: + ctx->bShowMouse = TRUE; + break; + case WM_MOUSEMOVE: + ctx->bShowMouse = FALSE; + if( ((lParam & 0xFFFF) >= 320*drv->mode->xfact) || ((lParam >> 16) >= 200*drv->mode->yfact)) + break; + drv->pointer_x = lParam & 0xFFFF; + drv->pointer_y = lParam >> 16; + break; + + case WM_LBUTTONDOWN: add_mouse_event (ctx, SCI_EVT_MOUSE_PRESS, 1, wParam); break; + case WM_RBUTTONDOWN: add_mouse_event (ctx, SCI_EVT_MOUSE_PRESS, 2, wParam); break; + case WM_MBUTTONDOWN: add_mouse_event (ctx, SCI_EVT_MOUSE_PRESS, 3, wParam); break; + case WM_LBUTTONUP: add_mouse_event (ctx, SCI_EVT_MOUSE_RELEASE, 1, wParam); break; + case WM_RBUTTONUP: add_mouse_event (ctx, SCI_EVT_MOUSE_RELEASE, 2, wParam); break; + case WM_MBUTTONUP: add_mouse_event (ctx, SCI_EVT_MOUSE_RELEASE, 3, wParam); break; + + case WM_KEYDOWN: + switch (wParam) + { + MAP_KEY (VK_ESCAPE, SCI_K_ESC); + MAP_KEY (VK_END, SCI_K_END); + MAP_KEY (VK_DOWN, SCI_K_DOWN); + MAP_KEY (VK_NEXT, SCI_K_PGDOWN); + MAP_KEY (VK_LEFT, SCI_K_LEFT); + MAP_KEY (VK_RIGHT, SCI_K_RIGHT); + MAP_KEY (VK_HOME, SCI_K_HOME); + MAP_KEY (VK_UP, SCI_K_UP); + MAP_KEY (VK_PRIOR, SCI_K_PGUP); + MAP_KEY (VK_INSERT, SCI_K_INSERT); + MAP_KEY (VK_DELETE, SCI_K_DELETE); + MAP_KEY (VK_BACK, SCI_K_BACKSPACE); + MAP_KEY (VK_TAB, '\t'); + MAP_KEY (VK_RETURN, '\r'); + + default: + if (wParam >= VK_F1 && wParam <= VK_F10) + add_key_event (ctx, wParam - VK_F1 + SCI_K_F1); + else if (wParam >= 'A' && wParam <= 'Z') + add_key_event (ctx, wParam - 'A' + 97); + else if (wParam >= VK_NUMPAD0 && wParam <= VK_NUMPAD9) + { + if (GetKeyState (VK_NUMLOCK) & 1) + add_key_event (ctx, wParam - VK_NUMPAD0 + '0'); + else + switch (wParam) + { + MAP_KEY (VK_NUMPAD0, SCI_K_INSERT); + MAP_KEY (VK_NUMPAD1, SCI_K_END); + MAP_KEY (VK_NUMPAD2, SCI_K_DOWN); + MAP_KEY (VK_NUMPAD3, SCI_K_PGDOWN); + MAP_KEY (VK_NUMPAD4, SCI_K_LEFT); + MAP_KEY (VK_NUMPAD6, SCI_K_RIGHT); + MAP_KEY (VK_NUMPAD7, SCI_K_HOME); + MAP_KEY (VK_NUMPAD8, SCI_K_UP); + MAP_KEY (VK_NUMPAD9, SCI_K_PGUP); + } + } + else if (wParam == 0xC0) /* tilde key - used for invoking console */ + add_key_event (ctx, '`'); + else + add_key_event (ctx, wParam); + break; + } + } + return DefWindowProc(hWnd,message,wParam,lParam); +} + +/* --------------------------------------------------------------- */ +extern "C" +gfx_driver_t gfx_driver_dd = { + "ddraw", + DD_DRIVER_VERSION, + SCI_GFX_DRIVER_MAGIC, + SCI_GFX_DRIVER_VERSION, + NULL, + 0,0, + 0, + GFX_DEBUG_POINTER | GFX_DEBUG_UPDATES | GFX_DEBUG_PIXMAPS | GFX_DEBUG_BASIC, + dd_set_param, + dd_init_specific, + dd_init, + dd_exit, + dd_draw_line, + dd_draw_filled_rect, + NULL, + NULL, + dd_draw_pixmap, + dd_grab_pixmap, + dd_update, + dd_set_static_buffer, + NULL, + dd_set_palette, + dd_get_event, + dd_usleep +}; + +#endif // HAVE_DDRAW diff --git a/engines/sci/gfx/drivers/dd_driver.h b/engines/sci/gfx/drivers/dd_driver.h new file mode 100644 index 0000000000..209fe51569 --- /dev/null +++ b/engines/sci/gfx/drivers/dd_driver.h @@ -0,0 +1,2 @@ + +void _dd_draw_line(gfx_mode_t * mode, rect_t line, byte * dest, int linewidth,gfx_color_t color); diff --git a/engines/sci/gfx/drivers/dd_driver_line.cpp b/engines/sci/gfx/drivers/dd_driver_line.cpp new file mode 100644 index 0000000000..e83b368a5e --- /dev/null +++ b/engines/sci/gfx/drivers/dd_driver_line.cpp @@ -0,0 +1,163 @@ +#ifndef __cplusplus +#error NOTE: This file MUST be compiled as C++. In Visual C++, use the /Tp command line option. +#endif + +extern "C" { +#include <gfx_system.h> +#include <gfx_tools.h> +}; + +#define LINEMACRO_1(startx, starty, deltalinear, deltanonlinear, linearvar, nonlinearvar, \ + linearend, nonlinearstart, linearmod, nonlinearmod) \ + x = (startx); y = (starty); \ + incrNE = ((deltalinear) > 0)? (deltalinear) : -(deltalinear); \ + incrNE <<= 1; \ + deltanonlinear <<= 1; \ + incrE = ((deltanonlinear) > 0) ? -(deltanonlinear) : (deltanonlinear); \ + d = nonlinearstart-1; \ + while (linearvar != (linearend)) { \ + buffer[linewidth * y + x] = color; \ + linearvar += linearmod; \ + if ((d+=incrE) < 0) { \ + d += incrNE; \ + nonlinearvar += nonlinearmod; \ + }; \ + }; \ + buffer[linewidth * y + x] = color; + +#define LINEMACRO_N(startx, starty, deltalinear, deltanonlinear, linearvar, nonlinearvar, \ + linearend, nonlinearstart, linearmod, nonlinearmod) \ + x = (startx); y = (starty); \ + incrNE = ((deltalinear) > 0)? (deltalinear) : -(deltalinear); \ + incrNE <<= 1; \ + deltanonlinear <<= 1; \ + incrE = ((deltanonlinear) > 0) ? -(deltanonlinear) : (deltanonlinear); \ + d = nonlinearstart-1; \ + while (linearvar != (linearend)) { \ + memcpy(&buffer[linewidth * y + bpp*x],&color,bpp); \ + linearvar += linearmod; \ + if ((d+=incrE) < 0) { \ + d += incrNE; \ + nonlinearvar += nonlinearmod; \ + }; \ + }; \ + memcpy(&buffer[linewidth * y + x],&color,bpp); + +void _dd_draw_line_buffer_1(byte *buffer, int linewidth, rect_t line, int color) +{ + /*void dither_line(picture_t buffers, int curx, int cury, short x1, short y1, + int col1, int col2, int priority, int special, char drawenable)*/ + + int dx, dy, incrE, incrNE, d, finalx, finaly; + int x = line.x; + int y = line.y; + dx = line.xl; + dy = line.yl; + finalx = x + dx; + finaly = y + dy; + + dx = abs(dx); + dy = abs(dy); + + if (dx > dy) { + if (finalx < x) { + if (finaly < y) { /* llu == left-left-up */ + LINEMACRO_1(x, y, dx, dy, x, y, finalx, dx, -1, -1); + } else { /* lld */ + LINEMACRO_1(x, y, dx, dy, x, y, finalx, dx, -1, 1); + } + } else { /* x1 >= x */ + if (finaly < y) { /* rru */ + LINEMACRO_1(x, y, dx, dy, x, y, finalx, dx, 1, -1); + } else { /* rrd */ + LINEMACRO_1(x, y, dx, dy, x, y, finalx, dx, 1, 1); + } + } + } else { /* dx <= dy */ + if (finaly < y) { + if (finalx < x) { /* luu */ + LINEMACRO_1(x, y, dy, dx, y, x, finaly, dy, -1, -1); + } else { /* ruu */ + LINEMACRO_1(x, y, dy, dx, y, x, finaly, dy, -1, 1); + } + } else { /* y1 >= y */ + if (finalx < x) { /* ldd */ + LINEMACRO_1(x, y, dy, dx, y, x, finaly, dy, 1, -1); + } else { /* rdd */ + LINEMACRO_1(x, y, dy, dx, y, x, finaly, dy, 1, 1); + } + } + } +} + +void _dd_draw_line_buffer_n(int bpp,byte *buffer, int linewidth, rect_t line, byte * color) +{ + /*void dither_line(picture_t buffers, int curx, int cury, short x1, short y1, + int col1, int col2, int priority, int special, char drawenable)*/ + + int dx, dy, incrE, incrNE, d, finalx, finaly; + int x = line.x; + int y = line.y; + dx = line.xl; + dy = line.yl; + finalx = x + dx; + finaly = y + dy; + + dx = abs(dx); + dy = abs(dy); + + if (dx > dy) { + if (finalx < x) { + if (finaly < y) { /* llu == left-left-up */ + LINEMACRO_N(x, y, dx, dy, x, y, finalx, dx, -1, -1); + } else { /* lld */ + LINEMACRO_N(x, y, dx, dy, x, y, finalx, dx, -1, 1); + } + } else { /* x1 >= x */ + if (finaly < y) { /* rru */ + LINEMACRO_N(x, y, dx, dy, x, y, finalx, dx, 1, -1); + } else { /* rrd */ + LINEMACRO_N(x, y, dx, dy, x, y, finalx, dx, 1, 1); + } + } + } else { /* dx <= dy */ + if (finaly < y) { + if (finalx < x) { /* luu */ + LINEMACRO_N(x, y, dy, dx, y, x, finaly, dy, -1, -1); + } else { /* ruu */ + LINEMACRO_N(x, y, dy, dx, y, x, finaly, dy, -1, 1); + } + } else { /* y1 >= y */ + if (finalx < x) { /* ldd */ + LINEMACRO_N(x, y, dy, dx, y, x, finaly, dy, 1, -1); + } else { /* rdd */ + LINEMACRO_N(x, y, dy, dx, y, x, finaly, dy, 1, 1); + } + } + } +} + +void _dd_draw_line(gfx_mode_t * mode, rect_t line, byte * dest, int linewidth,gfx_color_t color) +{ + byte ca[4]; + unsigned short co1; + + switch(mode->bytespp) + { + case 1: + _dd_draw_line_buffer_1(dest,linewidth,line,color.visual.global_index); + break; + case 2: + co1 = (color.visual.r << mode->red_shift) & mode->red_mask; + co1 |= (color.visual.g << mode->green_shift) & mode->green_mask; + co1 |= (color.visual.b << mode->blue_shift) & mode->blue_mask; + _dd_draw_line_buffer_n(2,dest,linewidth,line, (byte *) &co1); + break; + case 3: + ca[mode->red_shift/8]=color.visual.r; + ca[mode->green_shift/8]=color.visual.g; + ca[mode->blue_shift/8]=color.visual.b; + _dd_draw_line_buffer_n(3,dest,linewidth,line, ca); + break; + } +} diff --git a/engines/sci/gfx/drivers/directfb_driver.c b/engines/sci/gfx/drivers/directfb_driver.c new file mode 100644 index 0000000000..634b2b11df --- /dev/null +++ b/engines/sci/gfx/drivers/directfb_driver.c @@ -0,0 +1,1033 @@ +/*************************************************************************** + directfb_driver.c Copyright (C) 2002 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) <jameson@linuxgames.com> + +***************************************************************************/ +/* FreeSCI 0.3.1+ graphics driver module for libdirectfb */ + + +#include <stdarg.h> +#include <gfx_system.h> +#include <gfx_driver.h> +#include <gfx_tools.h> +#include <assert.h> +#include <uinput.h> + +#ifdef HAVE_DIRECTFB + +#include <stdio.h> +#include <stdlib.h> +#include <directfb/directfb.h> +#include <unistd.h> + +#define SCIDFB_DRIVER_VERSION "0.4" + +typedef struct _event_queue_struct { + sci_event_t evt; + struct _event_queue_struct *next; +} event_queue_t; + +/* +** For this driver to work to the fullest of its capabilities, the user running it +** must have rw permissions on the following: +** /dev/tty0 (including VT_ACTIVATE perms) +** /dev/tty7 +** /dev/fb0 +** /dev/psaux (if using a PS/2 mouse) +** +** VT_ACTIVATE permissions are, AFAIK, only available to root ATM :-( +*/ + +#define SCIDFB_CAP_ALPHA (1 << 0) /* Do we have an Alpha channel? */ + +#define SCIDFB_MAY_ALPHA (scidfb_internal_caps & SCIDFB_CAP_ALPHA) + +/* Global data */ + +static IDirectFB *scidfb_framebuffer; /* The global framebuffer */ +static IDirectFBDisplayLayer *scidfb_layer; /* The layer we use for drawing */ +static IDirectFBSurface *scidfb_visual; /* Static visual buffer */ +static IDirectFBSurface *scidfb_static_visual; /* Static visual buffer */ +static gfx_pixmap_t *scidfb_static_priority = NULL; +static gfx_pixmap_t *scidfb_back_priority = NULL; +static int scidfb_internal_caps = 0; /* Certain internal capabilities available to us */ + +/* Input structures */ +static IDirectFBEventBuffer *scidfb_input_buffer = NULL; +static event_queue_t *scidfb_event_queue = NULL; +static unsigned int scidfb_buckybits = SCI_EVM_INSERT; + + +/*#define LIST_ALL_DFB_MODES */ /* Uncomment for minimal debugging */ + +#define SCIDFB_CHECKED(f) \ + { \ + DFBResult _r = (f); \ + if (_r != DFB_OK) { \ + scidfb_handle_error(_r, __FILE__, __LINE__); \ + return GFX_ERROR; \ + } \ + } + +#define SCIGFX_CHECKED(f) \ + { \ + DFBResult _r = (f); \ + if (_r != GFX_OK) { \ + return _r; \ + } \ + } + + +static void +scidfb_handle_error(DFBResult errc, char *file, int line) +{ + const char *err = DirectFBErrorString(errc); + + GFXERROR("DFB-GFX, %s L%d: %s\n", file, line, err); +} + + +typedef struct { + int x, y, bpp; +} scidfb_mode_t; + + +static DFBEnumerationResult +scidfb_mode_callback(unsigned int width, unsigned int height, unsigned int bpp, void *conf) +{ + scidfb_mode_t *aim = (scidfb_mode_t *) conf; + scidfb_mode_t *best = aim + 1; + + if ((width >= aim->x && height >= aim->y && bpp >= aim->bpp) + /* Sufficient in all respects... */ + && ((best->bpp == 0) /* First mode that matched */ + || (width < best->x && height <= best->y) /* Improvement on x */ + || (width <= best->x && height < best->y) /* Improvement on y */ + || (width <= best->x && height <= best->y && bpp < best->bpp) + /* Improvement on bpp */ + )) { + best->x = width; + best->y = height; + best->bpp = bpp; + } + +#ifdef LIST_ALL_DFB_MODES + GFXERROR("DFB-GFX: Supports %dx%d at %d bpp\n", width, height, bpp); + return DFENUM_OK; +#endif + + if (aim->x == best->x + && aim->y == best->y + && aim->bpp == best->bpp) + return DFENUM_CANCEL; /* We have what we were looking for */ + + return DFENUM_OK; /* Continue searching */ +} + + +static DFBEnumerationResult +scidfb_layer_callback(unsigned int layer_id, DFBDisplayLayerCapabilities caps, void *resultp) +{ + unsigned int *results = (unsigned int *) resultp; + + if (!(caps & DLCAPS_SURFACE)) + return DFENUM_OK; /* We need a surface */ + + if (!results[1]) + results[0] = layer_id; + + results[1] = 1; + + if (caps & DLCAPS_ALPHACHANNEL) { + /* Optimal! */ + scidfb_internal_caps |= SCIDFB_MAY_ALPHA; + return DFENUM_CANCEL; /* We're done */ + } + + /* Found something, but it might get better */ + return DFENUM_OK; +} + +#define LOOKING_FOR_POINTING_DEVICE 1 +#define LOOKING_FOR_KEYBOARD 2 +#define FOUND_IT 255 + +static DFBEnumerationResult +scidfb_input_callback(unsigned int device_id, DFBInputDeviceDescription descr, void *p) +{ + int *state = (int *) p; + int *result = state + 1; + + /* Check for mouse */ + if (*state == LOOKING_FOR_POINTING_DEVICE + && (descr.type & DIDTF_MOUSE)) { + *result = device_id; + *state = FOUND_IT; + return DFENUM_CANCEL; + } + + /* Check for keyboard */ + if (*state == LOOKING_FOR_KEYBOARD + && (descr.type & DIDTF_KEYBOARD)) { + *result = device_id; + *state = FOUND_IT; + return DFENUM_CANCEL; + } + + return DFENUM_OK; +} + +static int +scidfb_find_layer() +{ + unsigned int results[2]; + + results[1] = 0; + SCIDFB_CHECKED(scidfb_framebuffer->EnumDisplayLayers(scidfb_framebuffer, + scidfb_layer_callback, &results)); + + if (!results[1]) + return GFX_FATAL; /* No decent layer! */ + else { + SCIDFB_CHECKED(scidfb_framebuffer->GetDisplayLayer(scidfb_framebuffer, + results[0], + &scidfb_layer)); + + return GFX_OK; + } +} + +static int +_scidfb_init_input(int *found_keyboard, int *found_mouse) +{ + int inputs[2]; + + inputs[0] = LOOKING_FOR_POINTING_DEVICE; + SCIDFB_CHECKED(scidfb_framebuffer->EnumInputDevices(scidfb_framebuffer, + scidfb_input_callback, &inputs)); + + if ((*found_mouse = (inputs[0] == FOUND_IT))) { + IDirectFBInputDevice *mouse; + SCIDFB_CHECKED(scidfb_framebuffer->GetInputDevice(scidfb_framebuffer, + inputs[1], &mouse)); + + SCIDFB_CHECKED(mouse->CreateEventBuffer(mouse, &scidfb_input_buffer)); + } + + inputs[0] = LOOKING_FOR_KEYBOARD; + SCIDFB_CHECKED(scidfb_framebuffer->EnumInputDevices(scidfb_framebuffer, + scidfb_input_callback, &inputs)); + if ((*found_keyboard = (inputs[0] == FOUND_IT))) { + IDirectFBInputDevice *keyboard; + SCIDFB_CHECKED(scidfb_framebuffer->GetInputDevice(scidfb_framebuffer, + inputs[1], &keyboard)); + + if (scidfb_input_buffer) + SCIDFB_CHECKED(keyboard->AttachEventBuffer(keyboard, scidfb_input_buffer)) + else + SCIDFB_CHECKED(keyboard->CreateEventBuffer(keyboard, + &scidfb_input_buffer)); + } + + + SCIDFB_CHECKED(scidfb_framebuffer->CreateEventBuffer(scidfb_framebuffer, + DICAPS_KEYS + | DICAPS_AXES + | DICAPS_BUTTONS, + &scidfb_input_buffer)); +} + +static int +_scidfb_init_gfx_mode(int width, int height, int bpp) +{ + scidfb_mode_t modes[2]; + modes[0].x = width; + modes[0].y = height; + modes[0].bpp = bpp; + + modes[1].bpp = 0; /* nothing found yet */ + + SCIDFB_CHECKED(DirectFBCreate(&scidfb_framebuffer)); + SCIDFB_CHECKED(scidfb_framebuffer->EnumVideoModes(scidfb_framebuffer, + scidfb_mode_callback, &modes)); + + if (modes[1].bpp) { + DFBDisplayLayerConfig conf; + + if (scidfb_find_layer()) { + GFXERROR("DFB-GFX: No useable display layer found"); + return GFX_FATAL; + } + + SCIDFB_CHECKED(scidfb_framebuffer->SetVideoMode(scidfb_framebuffer, + modes[1].x, modes[1].y, modes[1].bpp)); + + SCIDFB_CHECKED(scidfb_layer->SetCooperativeLevel(scidfb_layer, DLSCL_EXCLUSIVE)); + SCIDFB_CHECKED(scidfb_layer->GetSurface(scidfb_layer, &scidfb_visual)); + + return GFX_OK; + } else + GFXERROR("DFB-GFX: No matching visual could be found!\n"); + + return GFX_FATAL; +} + + +static int +scidfb_build_virtual_surface(IDirectFBSurface **result, int width, int height, + IDirectFBSurface *prototype) +{ + /* This function creates a _virtual_ surface, i.e. a memory buffer, + ** without flipping support */ + DFBSurfaceDescription srf_desc; + IDirectFBSurface *surface; + + SCIDFB_CHECKED(prototype->GetCapabilities(prototype, &(srf_desc.caps))); + srf_desc.caps &= ~DSCAPS_FLIPPING; + srf_desc.width = width; + srf_desc.height = height; + SCIDFB_CHECKED(prototype->GetPixelFormat(prototype, &(srf_desc.pixelformat))); + srf_desc.flags = (DSDESC_CAPS | DSDESC_WIDTH | DSDESC_HEIGHT | DSDESC_PIXELFORMAT); + SCIDFB_CHECKED(scidfb_framebuffer->CreateSurface(scidfb_framebuffer, &srf_desc, &surface)); + SCIDFB_CHECKED(surface->Clear(surface, 0, 0, 0, 255)); + + *result = surface; + + return GFX_OK; +} + +static int +scidfb_deactivate_pointer() +{ + SCIDFB_CHECKED(scidfb_layer->EnableCursor(scidfb_layer, 0)); + return 0; +} + + + /*----------------------------------------------------------------*/ +/*----------- Event queue implementation --------------*/ + /*----------------------------------------------------------------*/ + +#define MILLION 1000000 + +static int +scidfb_xlate_key(DFBInputDeviceKeyIdentifier key, int keysym) +{ + if ((keysym >= 'A') && (keysym <= 'Z')) + return keysym; + if ((keysym >= 'a') && (keysym <= 'z')) + return keysym; + if ((keysym >= '0') && (keysym <= '9')) + return keysym; + + switch (key) { + + case DIKI_0: return '0'; + case DIKI_1: return '1'; + case DIKI_2: return '2'; + case DIKI_3: return '3'; + case DIKI_4: return '4'; + case DIKI_5: return '5'; + case DIKI_6: return '6'; + case DIKI_7: return '7'; + case DIKI_8: return '8'; + case DIKI_9: return '9'; + + case DIKI_F1: return SCI_K_F1; + case DIKI_F2: return SCI_K_F2; + case DIKI_F3: return SCI_K_F3; + case DIKI_F4: return SCI_K_F4; + case DIKI_F5: return SCI_K_F5; + case DIKI_F6: return SCI_K_F6; + case DIKI_F7: return SCI_K_F7; + case DIKI_F8: return SCI_K_F8; + case DIKI_F9: return SCI_K_F9; + case DIKI_F10: return SCI_K_F10; + + case DIKI_KP_0: + case DIKI_INSERT: scidfb_buckybits ^= SCI_EVM_INSERT; + return SCI_K_INSERT; + + case DIKI_ESCAPE: return SCI_K_ESC; + case DIKI_LEFT: return SCI_K_LEFT; + case DIKI_RIGHT: return SCI_K_RIGHT; + case DIKI_UP: return SCI_K_UP; + case DIKI_DOWN: return SCI_K_DOWN; + case DIKI_TAB: return SCI_K_TAB; + case DIKI_ENTER: return SCI_K_ENTER; + case DIKI_SPACE: return ' '; + case DIKI_BACKSPACE: return SCI_K_BACKSPACE; + case DIKI_DELETE: return SCI_K_DELETE; + case DIKI_HOME: return SCI_K_HOME; + case DIKI_END: return SCI_K_END; + case DIKI_PAGE_UP: return SCI_K_PGDOWN; + case DIKI_PAGE_DOWN: return SCI_K_PGUP; + + case DIKI_QUOTE_LEFT: return '`'; + case DIKI_MINUS_SIGN: return '-'; + case DIKI_EQUALS_SIGN: return '='; + case DIKI_BRACKET_LEFT: return '['; + case DIKI_BRACKET_RIGHT: return ']'; + case DIKI_BACKSLASH: return '\\'; + case DIKI_SEMICOLON: return ';'; + case DIKI_QUOTE_RIGHT: return '\''; + case DIKI_COMMA: return ','; + case DIKI_SLASH: return '/'; + case DIKI_PERIOD: return '.'; + + case DIKI_KP_DIV: return '/'; + case DIKI_KP_MULT: return '*'; + case DIKI_KP_MINUS: return '-'; + case DIKI_KP_PLUS: return '+'; + case DIKI_KP_ENTER: return SCI_K_ENTER; + case DIKI_KP_SPACE: return ' '; + case DIKI_KP_TAB: return SCI_K_TAB; + case DIKI_KP_F1: return SCI_K_F1; + case DIKI_KP_F2: return SCI_K_F2; + case DIKI_KP_F3: return SCI_K_F3; + case DIKI_KP_F4: return SCI_K_F4; + case DIKI_KP_EQUAL: return '='; + + case DIKI_KP_DECIMAL: return SCI_K_DELETE; + case DIKI_KP_1: return SCI_K_END; + case DIKI_KP_2: return SCI_K_DOWN; + case DIKI_KP_3: return SCI_K_PGDOWN; + case DIKI_KP_4: return SCI_K_LEFT; + case DIKI_KP_5: return SCI_K_CENTER; + case DIKI_KP_6: return SCI_K_RIGHT; + case DIKI_KP_7: return SCI_K_HOME; + case DIKI_KP_8: return SCI_K_UP; + case DIKI_KP_9: return SCI_K_PGUP; + + default: + + } + return 0; /* Could not map key */ +} + + +scidfb_handle_bucky(int add, DFBInputDeviceKeyIdentifier key) +{ + int modifier = 0; + + switch(key) { + case DIKI_SHIFT_L: modifier = SCI_EVM_LSHIFT; + break; + case DIKI_SHIFT_R: modifier = SCI_EVM_RSHIFT; + break; + case DIKI_CONTROL_L: + case DIKI_CONTROL_R: modifier = SCI_EVM_CTRL; + break; + case DIKI_ALT_L: + case DIKI_ALT_R: + case DIKI_META_L: + case DIKI_META_R: modifier = SCI_EVM_ALT; + break; + case DIKI_CAPS_LOCK: modifier = SCI_EVM_CAPSLOCK; + break; + case DIKI_NUM_LOCK: modifier = SCI_EVM_NUMLOCK; + break; + case DIKI_SCROLL_LOCK: modifier = SCI_EVM_SCRLOCK; + break; + } + + if (add) + scidfb_buckybits |= modifier; + else + scidfb_buckybits &= ~modifier; +} + +static sci_event_t +scidfb_xlate_event(gfx_driver_t *drv, DFBEvent dfb_pre_evt) +{ + sci_event_t retval; + + retval.type = SCI_EVT_NONE; + retval.buckybits = scidfb_buckybits; + + if (dfb_pre_evt.clazz == DFEC_INPUT) { /* What kind of idiot named these field members? */ + DFBInputEvent dfbevt = dfb_pre_evt.input; + switch (dfbevt.type) { + + case DIET_KEYPRESS: + retval.type = SCI_EVT_KEYBOARD; + scidfb_handle_bucky(1, dfbevt.key_id); + retval.data = scidfb_xlate_key(dfbevt.key_id, dfbevt.key_symbol); + if (retval.data == 0) + retval.type = SCI_EVT_NONE; + break; + + case DIET_KEYRELEASE: + scidfb_handle_bucky(0, dfbevt.key_id); + break; + + case DIET_BUTTONPRESS: + retval.type = SCI_EVT_MOUSE_PRESS; + retval.data = dfbevt.button - DIBI_FIRST; + break; + + case DIET_AXISMOTION:{ + int *victim = NULL; + + if (dfbevt.axis == DIAI_X) + victim = &(drv->pointer_x); + else if (dfbevt.axis == DIAI_Y) + victim = &(drv->pointer_y); + else break; + + if (dfbevt.flags & DIEF_AXISABS) + *victim = dfbevt.axisabs; + else if (dfbevt.flags & DIEF_AXISREL) + *victim += dfbevt.axisrel; + } + break; + + case DIET_BUTTONRELEASE: + retval.type = SCI_EVT_MOUSE_RELEASE; + retval.data = dfbevt.button - DIBI_FIRST; + break; + } + } + + return retval; +} + +static void +scidfb_queue_event(sci_event_t evt) +{ + event_queue_t *node = malloc(sizeof(event_queue_t)); + event_queue_t **seekerp = &scidfb_event_queue; + + node->evt = evt; + node->next = NULL; + + while (*seekerp) + seekerp = &((*seekerp)->next); + + *seekerp = node; +} + + +static int +scidfb_queue_next_event(gfx_driver_t *drv, long micros_to_wait) +{ + DFBEvent dfb_event; + long secs = micros_to_wait / MILLION; + long usecs = (micros_to_wait - (secs * MILLION)) / 1000; + int has_event; + DFBResult timeout; + + timeout = scidfb_input_buffer->WaitForEventWithTimeout(scidfb_input_buffer, + secs, usecs); + if (timeout != DFB_TIMEOUT) + SCIDFB_CHECKED(timeout); + + has_event = scidfb_input_buffer->HasEvent(scidfb_input_buffer); + if (has_event == DFB_OK) { + sci_event_t evt; + /* We have a new event */ + SCIDFB_CHECKED(scidfb_input_buffer->GetEvent(scidfb_input_buffer, &dfb_event)); + evt = scidfb_xlate_event(drv, dfb_event); + + if (evt.type != SCI_EVT_NONE) + scidfb_queue_event(evt); + + return 0; + } else + if (has_event != DFB_BUFFEREMPTY) + return has_event; /* An error occured */ +} + + +static int +_scidfb_read_event(sci_event_t *evtp) +{ + if (scidfb_event_queue) { + event_queue_t *q = scidfb_event_queue; + *evtp = q->evt; + scidfb_event_queue = q->next; + free(q); + + return 1; + } else { + evtp->type = SCI_EVT_NONE; + return 0; + } +} + + + + + /*----------------------------------------------------------------*/ +/*----------- Implementations required by gfx_driver_t --------------*/ + /*----------------------------------------------------------------*/ + + +static int +scidfb_set_parameter(gfx_driver_t *drv, char *name, char *value) +{ + fprintf(stderr, "FIXME: These must be caught and moved in between DirectFBInit() and ..Create()\n"); + exit(1); + SCIDFB_CHECKED(DirectFBSetOption(name, value)); + return GFX_OK; +} + +static int +_scidfb_decode_pixel_format(DFBSurfacePixelFormat pixel_format, + int *rm, int *gm, int *bm, int *am, + int *rs, int *gs, int *bs, int *as, int *bytespp) +{ + /* Initially, the masks are set to the number of bits they occupy, and shifts + ** are left shifts */ + int _rs, _gs, _bs, _as; + + _as = 0; + *am = 0; + *as = 0; + + switch (pixel_format) { + + case DSPF_RGB15: + *bytespp = 2; + *rm = 5; _rs = 10; + *gm = 5; _gs = 5; + *bm = 5; _bs = 0; + break; + + case DSPF_RGB16: + *bytespp = 2; + *rm = 5; _rs = 11; + *gm = 6; _gs = 5; + *bm = 5; _bs = 0; + break; + + case DSPF_RGB24: + *bytespp = 3; + *rm = 8; _rs = 16; + *gm = 8; _gs = 8; + *bm = 8; _bs = 0; + break; + + case DSPF_RGB32: + *bytespp = 4; + *rm = 8; _rs = 16; + *gm = 8; _gs = 8; + *bm = 8; _bs = 0; + break; + + case DSPF_ARGB: + *bytespp = 4; + *am = 8; _as = 24; + *rm = 8; _rs = 16; + *gm = 8; _gs = 8; + *bm = 8; _bs = 0; + break; + + case DSPF_RGB332: + *bytespp = 1; + *rm = 3; _rs = 5; + *gm = 3; _gs = 2; + *bm = 2; _bs = 0; + break; + + default: + GFXERROR("DFB-GFX: Unknown/unsupported pixel format %08x\n", pixel_format); + return GFX_FATAL; + } + + /* Calculate correct shifts */ + *rs = 32 - *rm - _rs; + *gs = 32 - *gm - _gs; + *bs = 32 - *bm - _bs; + *as = 32 - *am - _as; + + /* Calculate correct masks */ + *rm = ((1 << (*rm)) - 1) << _rs; + *gm = ((1 << (*gm)) - 1) << _gs; + *bm = ((1 << (*bm)) - 1) << _bs; + *am = ((1 << (*am)) - 1) << _as; + + return GFX_OK; +} + +static int +scidfb_init_specific(gfx_driver_t *drv, int xres, int yres, int bytespp) +{ + DFBSurfacePixelFormat pixel_format; + char **p; + char *foo = ""; + int n = 1; + + int width, height; + int red_mask, green_mask, blue_mask, alpha_mask; + int red_shift, green_shift, blue_shift, alpha_shift; + int bytespp_real; + int mouse, keyboard; + + p = &foo; + DirectFBInit(&n, &p); + + SCIDFB_CHECKED(DirectFBSetOption("no-sighandler", "1")); + + SCIGFX_CHECKED(_scidfb_init_gfx_mode(xres, yres, (1 << bytespp) - 1)); + SCIGFX_CHECKED(_scidfb_init_input(&keyboard, &mouse)); + + if (!keyboard) { + GFXERROR("DFB-GFX: No keyboard found-- aborting...\n"); + return GFX_FATAL; + } + if (!mouse) { + GFXWARN("DFB-GFX: No pointing device found, disabling mouse support...\n"); + drv->capabilities &= ~GFX_CAPABILITY_MOUSE_SUPPORT; + } + + SCIDFB_CHECKED(scidfb_visual->GetSize(scidfb_visual, &width, &height)); + + scidfb_deactivate_pointer(); + SCIDFB_CHECKED(scidfb_visual->Clear(scidfb_visual, 0, 0, 0, 255)); + SCIDFB_CHECKED(scidfb_visual->Flip(scidfb_visual, NULL, DSFLIP_BLIT)); + + + /* If neccessary, create a sub-surface */ + if (width > xres * 320 || height > yres * 200) { + IDirectFBSurface *subsurface; + DFBRectangle region; + region.w = xres * 320; + region.h = yres * 200; + region.x = (width - region.w) >> 1; + region.y = (height - region.h) >> 1; + + SCIDFB_CHECKED(scidfb_visual->GetSubSurface(scidfb_visual, ®ion, &subsurface)); + + scidfb_visual = subsurface; + } + + scidfb_visual->SetDrawingFlags(scidfb_visual, DSDRAW_BLEND); /* Unchecked: It's just a + ** feature */ + SCIDFB_CHECKED(scidfb_visual->GetPixelFormat(scidfb_visual, &pixel_format)); + + SCIGFX_CHECKED(_scidfb_decode_pixel_format(pixel_format, + &red_mask, &green_mask, &blue_mask, &alpha_mask, + &red_shift, &green_shift, &blue_shift, &alpha_shift, + &bytespp_real)); + + fprintf(stderr, "Mode %08x -> (%x>>%d, %x>>%d, %x>>%d, %x>>%d) at %d bytespp\n", + pixel_format, + red_mask, red_shift, + green_mask, green_shift, + blue_mask, blue_shift, + alpha_mask, alpha_shift, + bytespp_real); + + drv->mode = gfx_new_mode(xres, yres, + bytespp_real, + red_mask, green_mask, blue_mask, alpha_mask, + red_shift, green_shift, blue_shift, alpha_shift, + 0 /* not palette mode*/, + 0); + + SCIGFX_CHECKED(scidfb_build_virtual_surface(&scidfb_static_visual, + drv->mode->xfact * 320, + drv->mode->yfact * 200, + scidfb_visual)); + + scidfb_back_priority = + gfx_pixmap_alloc_index_data(gfx_new_pixmap(drv->mode->xfact * 320, + drv->mode->yfact * 200, + GFX_RESID_NONE, 0, 0)); + + return GFX_OK; +} + +static int +_scidfb_set_color(gfx_color_t *c) +{ + SCIDFB_CHECKED(scidfb_visual->SetColor(scidfb_visual, + c->visual.r, c->visual.g, c->visual.b, + (c->mask & GFX_MASK_VISUAL)? (255 - c->alpha) : 0)); + return GFX_OK; +} + +static int +scidfb_init(gfx_driver_t *drv) +{ + return scidfb_init_specific(drv, 2, 2, 1); +} + + +static void +scidfb_exit(gfx_driver_t *drv) +{ + gfx_free_pixmap(drv, scidfb_back_priority); +} + +static int +scidfb_draw_line(gfx_driver_t *drv, point_t start, point_t end, gfx_color_t color, + gfx_line_mode_t line_mode, gfx_line_style_t line_style) +{ + int xc, yc; + + SCIGFX_CHECKED(_scidfb_set_color(&color)); + + if (line_mode == GFX_LINE_MODE_FINE) { + SCIDFB_CHECKED(scidfb_visual->DrawLine(scidfb_visual, + start.x, start.y, + end.x, end.y)); + } else /* "Correct" lines */ + for (xc = 0; xc < drv->mode->xfact; xc++) + for (yc = 0; yc < drv->mode->yfact; yc++) + SCIDFB_CHECKED(scidfb_visual->DrawLine(scidfb_visual, + start.x + xc, + start.y + yc, + end.x + xc, + end.y + yc)); + + return GFX_OK; +} + +static int +scidfb_draw_filled_rect(gfx_driver_t *drv, rect_t rect, + gfx_color_t color1, gfx_color_t color2, + gfx_rectangle_fill_t shade_mode) +{ + SCIGFX_CHECKED(_scidfb_set_color(&color1)); + SCIDFB_CHECKED(scidfb_visual->FillRectangle(scidfb_visual, + rect.x, rect.y, + rect.xl, rect.yl)); + + return GFX_OK; +} + +static int +scidfb_register_pixmap(gfx_driver_t *drv, gfx_pixmap_t *pxm) +{ + return GFX_OK; +} + + +static int +scidfb_unregister_pixmap(gfx_driver_t *drv, gfx_pixmap_t *pxm) +{ + return GFX_OK; +} + + +static int +scidfb_draw_pixmap(gfx_driver_t *drv, gfx_pixmap_t *pxm, int priority, + rect_t src, rect_t dest, gfx_buffer_t buffer) +{ + void *_dest_data; + byte *dest_data; + int line_width; + IDirectFBSurface *visual = (buffer == GFX_BUFFER_STATIC)? + scidfb_static_visual : scidfb_visual; + gfx_pixmap_t *priority_map = (buffer == GFX_BUFFER_STATIC)? + scidfb_static_priority : scidfb_back_priority; + + SCIDFB_CHECKED(visual->Lock(visual, DSLF_WRITE | DSLF_READ, + &_dest_data, &line_width)); + dest_data = (byte *) _dest_data; + + gfx_crossblit_pixmap(drv->mode, pxm, priority, src, dest, dest_data, + line_width, + priority_map->index_data, priority_map->index_xl, + 1, 0); + + SCIDFB_CHECKED(visual->Unlock(visual)); +} + +static int +scidfb_grab_pixmap(gfx_driver_t *drv, rect_t src, gfx_pixmap_t *pxm, + gfx_map_mask_t map) +{ + void *_src_data; + byte *src_data; + byte *dest = pxm->data; + int line_width; + int write_width = drv->mode->bytespp * (src.xl); + + pxm->xl = src.xl; + pxm->yl = src.yl; + + if (map != GFX_MASK_VISUAL) { + fprintf(stderr, "Not trying to read from visual mask-- not supported!"); + return GFX_FATAL; + } + + SCIDFB_CHECKED(scidfb_visual->Lock(scidfb_visual, DSLF_READ | DSLF_WRITE, + /* Must use DSLF_WRITE to choose the back buffer, + ** otherwise the front buffer would be chosen */ + &_src_data, &line_width)); + src_data = (byte *) _src_data; + + src_data += (drv->mode->bytespp * src.x) + + (line_width * src.y); /* left upper corner of the source */ + + if (src.y < 0){ + fprintf(stderr, "src.y=%d\n", src.y); + exit(1); + } + + while (src.yl--) { + memcpy(dest, src_data, write_width); + src_data += line_width; + dest += write_width; + } + + + SCIDFB_CHECKED(scidfb_visual->Unlock(scidfb_visual)); +} + +static int +scidfb_update(gfx_driver_t *drv, rect_t src, point_t dest, gfx_buffer_t buffer) +{ + if (src.x != dest.x + || src.y != dest.y) { + GFXERROR("DFB-GFX: Attempt to update from (%d,%d,%d,%d) to (%d,%d)\n", + GFX_PRINT_RECT(src), dest.x, dest.y); + } + + if (buffer == GFX_BUFFER_FRONT) { + DFBRegion region; + region.x1 = src.x; + region.y1 = src.y; + region.x2 = src.x + src.xl; + region.y2 = src.y + src.yl; + + SCIDFB_CHECKED(scidfb_visual->Flip(scidfb_visual, ®ion, DSFLIP_BLIT)); + } else { /* Back buffer update */ + DFBRectangle dest_rect; + dest_rect.x = src.x; + dest_rect.y = src.y; + dest_rect.w = src.xl; + dest_rect.h = src.yl; + + SCIDFB_CHECKED(scidfb_visual->Blit(scidfb_visual, + scidfb_static_visual, + &dest_rect, + dest.x, dest.y)); + + /* Now update the priority map: */ + if (scidfb_static_priority) /* only if the buffers have been initialized */ + gfx_copy_pixmap_box_i(scidfb_back_priority, scidfb_static_priority, src); + } + + return GFX_OK; +} + +static int +scidfb_set_static_buffer(gfx_driver_t *drv, gfx_pixmap_t *pic, gfx_pixmap_t *priority) +{ + void *_dest; + byte *dest; + byte *src = pic->data; + int line_width; + int draw_width = pic->xl * drv->mode->bytespp; + int i; + + scidfb_static_priority = priority; + SCIDFB_CHECKED(scidfb_static_visual->Lock(scidfb_static_visual, DSLF_WRITE, + &_dest, &line_width)); + dest = (byte *) _dest; + + for (i = 0; i < pic->yl; i++) { + memcpy(dest, src, draw_width); + dest += line_width; + src += draw_width; + } + + SCIDFB_CHECKED(scidfb_static_visual->Unlock(scidfb_static_visual)); + + return GFX_OK; +} + +static void +_normalize_pointer(gfx_driver_t *drv) +{ + int maxw = drv->mode->xfact * 320; + int maxh = drv->mode->yfact * 200; + + if (drv->pointer_x < 0) + drv->pointer_x = 0; + if (drv->pointer_x >= maxw) + drv->pointer_x = maxw - 1; + if (drv->pointer_y < 0) + drv->pointer_y = 0; + if (drv->pointer_y >= maxh) + drv->pointer_y = maxh - 1; +} + + +static sci_event_t +scidfb_get_event(gfx_driver_t *drv) +{ + sci_event_t retval; + + scidfb_queue_next_event(drv, 0); + _scidfb_read_event(&retval); + + _normalize_pointer(drv); + + + return retval; +} + +static int +scidfb_usec_sleep(gfx_driver_t *drv, long usecs) +{ + scidfb_queue_next_event(drv, usecs); + _normalize_pointer(drv); + return GFX_OK; +} + +gfx_driver_t gfx_driver_dfb = { + "dfb", + SCIDFB_DRIVER_VERSION, + SCI_GFX_DRIVER_MAGIC, + SCI_GFX_DRIVER_VERSION, + NULL, + 0, 0, + GFX_CAPABILITY_MOUSE_SUPPORT + | GFX_CAPABILITY_FINE_LINES, + 0, + scidfb_set_parameter, + scidfb_init_specific, + scidfb_init, + scidfb_exit, + scidfb_draw_line, + scidfb_draw_filled_rect, + scidfb_register_pixmap, + scidfb_unregister_pixmap, + scidfb_draw_pixmap, + scidfb_grab_pixmap, + scidfb_update, + scidfb_set_static_buffer, + NULL, + NULL, + scidfb_get_event, + scidfb_usec_sleep, + NULL +}; + +#endif diff --git a/engines/sci/gfx/drivers/dx_driver.cpp b/engines/sci/gfx/drivers/dx_driver.cpp new file mode 100644 index 0000000000..899d1fa70c --- /dev/null +++ b/engines/sci/gfx/drivers/dx_driver.cpp @@ -0,0 +1,1179 @@ +/*************************************************************************** + dx_driver.cpp Copyright (C) 2008 Alexander R Angas, + Some portions Copyright (C) 1999 Dmitry Jemerov + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Alexander R Angas (Alex) <arangas AT internode dot on dot net> + + History: + 20051106 (AAngas) - Rewrite + 20060201 (AAngas) - Fixed wrong format for texture priority maps + 20060205 (AAngas) - Changed pointer to use D3DXSprite + 20060208 (AAngas) - Fixed pointer alpha blending bug + 20060307 (AAngas) - Fixed crash on exit + 20080118 (AAngas) - Fixed pointer scaling and window size + + Notes: + DirectX handles all scaling. All drawing functions assume an unscaled + resolution. + +TODO: + P1 - Corrupt cursor at different resolutions + P1 - Lost devices + P1 - Problems moving and activating window + P1 - Occasional pause when starting FreeSCI and poor sound resulting (?) + P1 - Mouse pointer corruption at bottom and why need to scale vertical by 4? + P2 - Full screen + P3 - Draw lines as Direct3D vertices + P3 - Fine line mode + P3 - Allow user to specify hardware or software vertex processing + P3 - Fancies + +***************************************************************************/ + +#ifdef HAVE_DIRECTX + +#ifndef __cplusplus +#error NOTE: This file MUST be compiled as C++. In Visual C++, use the /Tp command line option. +#endif + +#include "dx_driver.h" + +#define TO_STRING2(x) #x +#define TO_STRING(x) TO_STRING2(x) +#if (DIRECT3D_VERSION < 0x0800) +# error The DirectX 8 SDK (or higher) is required for this driver. +#elif (DIRECT3D_VERSION > 0x0800) +# pragma message (" ") +# pragma message ("*** This driver has been developed against version 8 of the DirectX SDK and may not work against your version " TO_STRING(DIRECT3D_VERSION)) +# pragma message (" ") +#endif + + +// Stores driver flags +static int flags = 0; + + +// Windows message processing +LRESULT WINAPI MsgProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam ) +{ + return DefWindowProc( hWnd, msg, wParam, lParam ); +} + + +///// RENDERING + +// Render the scene to screen +static gfx_return_value_t +RenderD3D(struct _gfx_driver *drv) +{ + HRESULT hr; + + // Check we haven't lost the device (i.e. window focus) + if (CheckDevice(drv)) + { + // Begin scene + DODX( (dx_state->pDevice->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0,0,0), 1.0, 0)), RenderD3D ); + DODX( (dx_state->pDevice->BeginScene()), RenderD3D ); + + // Set texture + DODX( (dx_state->pDevice->SetTexture( 0, dx_state->pTexVisuals[PRIMARY_VIS] )), RenderD3D ); // Scene image + + // Set texture states for scene + DODX( (dx_state->pDevice->SetTextureStageState( 0, D3DTSS_COLOROP, D3DTOP_MODULATE )), RenderD3D ); + DODX( (dx_state->pDevice->SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TEXTURE )), RenderD3D ); + DODX( (dx_state->pDevice->SetTextureStageState( 0, D3DTSS_COLORARG2, D3DTA_DIFFUSE )), RenderD3D ); + + // Set vertices and how to draw + DODX( (dx_state->pDevice->SetStreamSource(0, dx_state->pVertBuff, sizeof(CUSTOMVERTEX))), RenderD3D ); + DODX( (dx_state->pDevice->SetVertexShader( D3DFVF_CUSTOMVERTEX )), RenderD3D ); + DODX( (dx_state->pDevice->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2)), RenderD3D ); + + // Draw the pointer sprite + if (dx_state->pTexPointer) + { + D3DXVECTOR2 pointerPos((float)drv->pointer_x, (float)drv->pointer_y); + DODX( (dx_state->pSPointer->Begin()), RenderD3D ); + DODX( (dx_state->pSPointer->Draw(dx_state->pTexPointer, NULL, NULL, NULL, 0.0, &pointerPos, 0xFFFFFFFF)), RenderD3D ); + DODX( (dx_state->pSPointer->End()), RenderD3D ); + } + + // Present scene + DODX( (dx_state->pDevice->EndScene()), RenderD3D ); + DODX( (dx_state->pDevice->Present(NULL, NULL, NULL, NULL)), RenderD3D ); + } + + return GFX_OK; +} + + +// Check device hasn't been lost +static int +CheckDevice(struct _gfx_driver *drv) +{ + HRESULT hr; + switch ( dx_state->pDevice->TestCooperativeLevel() ) + { + case D3DERR_DEVICELOST: return false; // Lost window focus + + case D3DERR_DEVICENOTRESET: // We're back! + { + // Reset with our presentation parameters and reinitialise the scene + DODX( (dx_state->pDevice->Reset(&dx_state->presParams)), CheckDevice ); + if (hr != D3D_OK) + return false; + + InitScene(drv); + return true; + } + + default: return true; + } +} + + +///// INITIALIZATION + +// Create window to draw to +static gfx_return_value_t +InitWindow(struct _gfx_driver *drv, UINT width, UINT height) +{ + DWORD dwExStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE; + DWORD dwStyle = WS_OVERLAPPEDWINDOW; + RECT clientSize = { 0, 0, width, height }; + sciprintf("Window %d x %d\n", width, height); + + // Register the window class + ZeroMemory( &(dx_state->wc), sizeof(WNDCLASSEX) ); + dx_state->wc.cbSize = sizeof(WNDCLASSEX); + dx_state->wc.style = CS_HREDRAW | CS_VREDRAW | CS_NOCLOSE; + dx_state->wc.lpfnWndProc = MsgProc; + dx_state->wc.hInstance = GetModuleHandle(NULL); + dx_state->wc.lpszClassName = DX_CLASS_NAME; + if ( RegisterClassEx( &dx_state->wc ) == 0 ) + { + sciprintf("InitWindow(): RegisterClassEx failed (%u)\n", GetLastError()); + return GFX_FATAL; + } + + // Get correct size of window to pass to CreateWindow (considering window decorations) + if ( AdjustWindowRectEx( &clientSize, dwStyle, false, dwExStyle ) == 0 ) + { + sciprintf("InitWindow(): Window size calculation failed (%u)\n", GetLastError()); + return GFX_FATAL; + } + + // Create the application's window + dx_state->hWnd = CreateWindowEx( + dwExStyle, + DX_CLASS_NAME, DX_APP_NAME, + dwStyle | WS_CLIPSIBLINGS | WS_CLIPCHILDREN, + CW_USEDEFAULT, CW_USEDEFAULT, + clientSize.right - clientSize.left, clientSize.bottom - clientSize.top, + NULL/*GetDesktopWindow()*/, NULL, dx_state->wc.hInstance, NULL ); + + if ( dx_state->hWnd == NULL ) + { + sciprintf("InitWindow(): CreateWindow failed (%u)\n", GetLastError()); + return GFX_FATAL; + } + + // Show the window + ShowWindow( dx_state->hWnd, SW_SHOWDEFAULT ); + UpdateWindow( dx_state->hWnd ); + + return GFX_OK; +} + + +// Initialize Direct3D +static gfx_return_value_t +InitD3D(struct _gfx_driver *drv) +{ + HRESULT hr; + + sciprintf("Initializing Direct3D\n"); + + // Set our colour format + dx_state->d3dFormat = D3DFMT_A8R8G8B8; + dx_state->vertexProcessing = D3DCREATE_MIXED_VERTEXPROCESSING; + + // Create Direct3D object + dx_state->pD3d = Direct3DCreate8( D3D_SDK_VERSION ); + if ( FAILED( dx_state->pD3d ) ) { + sciprintf("InitD3D(): Direct3DCreate8 failed\n"); + return GFX_FATAL; + } + + // Look for adapters + for ( UINT adapterLoop = 0; adapterLoop < dx_state->pD3d->GetAdapterCount(); adapterLoop++ ) + { + D3DADAPTER_IDENTIFIER8 adapterId; + DODX( (dx_state->pD3d->GetAdapterIdentifier(adapterLoop, D3DENUM_NO_WHQL_LEVEL, &adapterId)), InitD3D ); + if ( FAILED( hr ) ) + break; + if (adapterId.Driver[0] == '\0') + break; + sciprintf(" Adapter %u: %s\n", adapterLoop++, adapterId.Description); + } + if (dx_state->adapterId == -1) + dx_state->adapterId = D3DADAPTER_DEFAULT; + + // Get device caps + DODX( (dx_state->pD3d->GetDeviceCaps(dx_state->adapterId, D3DDEVTYPE_HAL, &(dx_state->deviceCaps))), InitD3D ); + if ( FAILED( hr ) ) { + sciprintf("Sorry, this adapter does not have a 3D accelerated video driver installed.\n"); + return GFX_FATAL; + } + + // Define presentation parameters + ZeroMemory( &dx_state->presParams, sizeof(D3DPRESENT_PARAMETERS) ); + dx_state->presParams.Windowed = TRUE; // We want windowed by default + dx_state->presParams.SwapEffect = D3DSWAPEFFECT_DISCARD; // Throw away last frame + dx_state->presParams.hDeviceWindow = dx_state->hWnd; // Window handle + dx_state->presParams.BackBufferWidth = 320; // Back buffer dimensions + dx_state->presParams.BackBufferHeight = 200; // + dx_state->presParams.BackBufferFormat = dx_state->d3dFormat; // Colour format + + // Check if user requested full screen + if (flags & DX_FLAGS_FULLSCREEN) + { + if (dx_state->deviceCaps.Caps2 & D3DCAPS2_CANRENDERWINDOWED) { + dx_state->presParams.Windowed = FALSE; + sciprintf("Full screen mode"); + } else { + sciprintf("Sorry, DirectX will not render in full screen with your video card\n"); + } + } + + // Get current display mode + DODX( (dx_state->pD3d->GetAdapterDisplayMode( dx_state->adapterId, &dx_state->displayMode )), InitD3D ); + sciprintf("Chosen adapter %u\n", dx_state->adapterId); + + // Turn off Windows mouse pointer + ShowCursor(FALSE); + + return GFX_OK; +} + + +// Initialize scene +static gfx_return_value_t +InitScene(struct _gfx_driver *drv) +{ + HRESULT hr; + + sciprintf("Creating scene\n"); + + // Describe how scene will be rendered + DODX((dx_state->pDevice->SetRenderState( D3DRS_AMBIENT, RGB(255,255,255) )), InitScene); // Maximum ambient light + DODX((dx_state->pDevice->SetRenderState( D3DRS_LIGHTING, FALSE )), InitScene); // Disable lighting features + DODX((dx_state->pDevice->SetRenderState( D3DRS_CULLMODE, D3DCULL_NONE )), InitScene); // Don't cull back side of polygons + DODX((dx_state->pDevice->SetRenderState( D3DRS_ZENABLE, D3DZB_FALSE )), InitScene); // No depth buffering + + return GFX_OK; +} + + +// For user to set a driver-specific parameter +static int +dx_set_param(struct _gfx_driver *drv, char *attribute, char *value) +{ +/* // Full screen + if (!strncmp(attribute, "fullscreen", 11)) { + if (string_truep(value)) + flags |= DX_FLAGS_FULLSCREEN; + else + flags &= ~DX_FLAGS_FULLSCREEN; + + return GFX_OK; + } +*/ + // Adapter ID + if (!strncmp(attribute, "adapterid", 11)) { + int aid = D3DADAPTER_DEFAULT; + dx_state->adapterId = atoi(value); + + return GFX_OK; + } + + sciprintf("Unrecognised attempt to set DirectX parameter \"%s\" to \"%s\"\n", attribute, value); + return GFX_ERROR; +} + + +// Initialize a specific graphics mode +static int +dx_init_specific(struct _gfx_driver *drv, + int xfact, int yfact, /* horizontal and vertical scaling */ + int bytespp) /* must be value 4 */ +{ + HRESULT hr; + int red_shift = 8, green_shift = 16, blue_shift = 24, alpha_shift = 32; + int alpha_mask = 0x00000000, red_mask = 0x00ff0000, green_mask = 0x0000ff00, blue_mask = 0x000000ff; + gfx_return_value_t d3dret; + + // Error checking + if (xfact < 1 || yfact < 1 || bytespp < 1 || bytespp > 4) { + sciprintf("Attempt to open window with invalid scale factors (%d,%d) and bpp=%d!\n", + xfact, yfact, bytespp); + return GFX_ERROR; + } + + // Prepare memory for gfx_dx_struct_t + drv->state = (struct gfx_dx_struct_t *) sci_malloc(sizeof(gfx_dx_struct_t)); + ZeroMemory(drv->state, sizeof(gfx_dx_struct_t)); + dx_state->adapterId = -1; // we will set this later + + // Set window size factor + dx_state->xfact = xfact; + dx_state->yfact = yfact; + dx_state->bpp = bytespp; + sciprintf("Window scaling %d x %d @ %d bpp\n", dx_state->xfact, dx_state->yfact, dx_state->bpp << 3); + + // Set up Direct3D + d3dret = InitD3D(drv); + if (d3dret != GFX_OK) + return d3dret; + + // Create window + InitWindow(drv, dx_state->xfact * 320, dx_state->yfact * 200); + + // Create D3D device + DODX( (dx_state->pD3d->CreateDevice(dx_state->adapterId, D3DDEVTYPE_HAL, dx_state->hWnd, + dx_state->vertexProcessing, &dx_state->presParams, &dx_state->pDevice)), dx_init_specific ); + + // Create the scene + d3dret = InitScene(drv); + if (d3dret != GFX_OK) + return d3dret; + + // Define and populate vertex buffers + DODX((dx_state->pDevice->CreateVertexBuffer( 4 * sizeof(CUSTOMVERTEX), D3DUSAGE_WRITEONLY, D3DFVF_CUSTOMVERTEX, + D3DPOOL_MANAGED, &dx_state->pVertBuff )), dx_init_specific); + + dx_state->pvData[0].p = D3DXVECTOR4( 0.0f, 0.0f, 0.0f, 1.0f); + dx_state->pvData[1].p = D3DXVECTOR4(320.0f, 0.0f, 0.0f, 1.0f); + dx_state->pvData[2].p = D3DXVECTOR4( 0.0f, 200.0f, 0.0f, 1.0f); + dx_state->pvData[3].p = D3DXVECTOR4(320.0f, 200.0f, 0.0f, 1.0f); + dx_state->pvData[0].colour = dx_state->pvData[1].colour = dx_state->pvData[2].colour = dx_state->pvData[3].colour = 0xffffffff; + dx_state->pvData[0].t = D3DXVECTOR2(0.0f, 0.0f); + dx_state->pvData[1].t = D3DXVECTOR2(1.0f, 0.0f); + dx_state->pvData[2].t = D3DXVECTOR2(0.0f, 1.0f); + dx_state->pvData[3].t = D3DXVECTOR2(1.0f, 1.0f); + + VOID *ptr; + DODX((dx_state->pVertBuff->Lock(0, 0, (BYTE**)&ptr, 0)), dx_init_specific); + memcpy(ptr, dx_state->pvData, sizeof(dx_state->pvData)); + DODX((dx_state->pVertBuff->Unlock()), dx_init_specific); + + // Create textures + int i; + for (i = 0; i < NUM_VISUAL_BUFFERS; i++) + { + DODX((dx_state->pDevice->CreateTexture(320, 200, + 1, 0, + dx_state->d3dFormat, + D3DPOOL_MANAGED, + &dx_state->pTexVisuals[i])), dx_init_specific); + } + for (i = 0; i < NUM_PRIORITY_BUFFERS; i++) + { + DODX((dx_state->pDevice->CreateTexture(320, 200, + 1, 0, + dx_state->d3dFormat, + D3DPOOL_MANAGED, + &dx_state->pTexPrioritys[i])), dx_init_specific); + } + + // Create sprite for pointer + DODX( (D3DXCreateSprite(dx_state->pDevice, &dx_state->pSPointer)), dx_init_specific ); + + // Allocate priority maps + for (int i = 0; i < NUM_PRIORITY_BUFFERS; i++) + { + dx_state->priority_maps[i] = gfx_pixmap_alloc_index_data(gfx_new_pixmap(320, 200, GFX_RESID_NONE, -i, -777)); + if (!dx_state->priority_maps[i]) { + GFXERROR("Out of memory: Could not allocate priority maps! (%dx%d)\n", 320, 200); + return GFX_FATAL; + } + dx_state->priority_maps[i]->flags |= GFX_PIXMAP_FLAG_SCALED_INDEX; + } + + // Set up the event queue + dx_state->queue_first = 0; + dx_state->queue_last = 0; + dx_state->queue_size = 256; + dx_state->event_queue = (sci_event_t *) sci_malloc (dx_state->queue_size * sizeof (sci_event_t)); + + // Set up graphics mode for primary vis + drv->mode = gfx_new_mode(1, 1, dx_state->bpp, + red_mask, green_mask, blue_mask, alpha_mask, + red_shift, green_shift, blue_shift, alpha_shift, + (dx_state->bpp == 1) ? 256 : 0, 0); + + // Set up graphics mode for pointer + dx_state->pointerMode = gfx_new_mode(1, 4, dx_state->bpp, + 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000, + 24, 16, 8, 0, + 0, GFX_MODE_FLAG_REVERSE_ALPHA); + + return GFX_OK; +} + + +// Initialize 'most natural' graphics mode +static int +dx_init(struct _gfx_driver *drv) +{ + return dx_init_specific(drv, 2, 2, 4); +} + + +// Uninitialize the current graphics mode +static void +dx_exit(struct _gfx_driver *drv) +{ + int i; + + if(drv->state == NULL) + return; + + for (i = 0; i < NUM_PRIORITY_BUFFERS; i++) + SAFE_RELEASE( dx_state->pTexPrioritys[i] ); + for (i = 0; i < NUM_VISUAL_BUFFERS; i++) + SAFE_RELEASE( dx_state->pTexVisuals[i] ); + SAFE_RELEASE( dx_state->pTexPointer ); + SAFE_RELEASE( dx_state->pSPointer ); + SAFE_RELEASE( dx_state->pVertBuff ); + SAFE_RELEASE( dx_state->pDevice ); + SAFE_RELEASE( dx_state->pD3d ); + + if ( dx_state->pointerMode ) + gfx_free_mode(dx_state->pointerMode); + if ( drv->mode ) + gfx_free_mode(drv->mode); + + if ( dx_state->event_queue ) + sci_free(dx_state->event_queue); + dx_state->queue_size = 0; + + for (i = 0; i < NUM_PRIORITY_BUFFERS; i++) + gfx_free_pixmap(drv, dx_state->priority_maps[i]); + + UnregisterClass( DX_CLASS_NAME, dx_state->wc.hInstance ); + DestroyWindow(dx_state->hWnd); + + sci_free(dx_state); +} + + +///// DRAWING + +// Draws a single filled and possibly shaded rectangle to the back buffer +static int +dx_draw_filled_rect(struct _gfx_driver *drv, rect_t box, + gfx_color_t color1, gfx_color_t color2, + gfx_rectangle_fill_t shade_mode) +{ + if (color1.mask & GFX_MASK_VISUAL) + { + HRESULT hr; + D3DLOCKED_RECT lockedRect; + + // Calculate colour value for line pixel + UINT lineColor = (color1.alpha << 24) | (color1.visual.r << 16) | (color1.visual.g << 8) | color1.visual.b; + RECT r = { box.x, box.y, box.x + box.xl, box.y + box.yl }; + RECT lr = r; + + // Fix bounds + if (lr.left == lr.right) + lr.right++; + if (lr.top == lr.bottom) + lr.bottom++; + if ((UINT)r.right > 320) + lr.right = r.right = 320; + if ((UINT)r.bottom > 200) + lr.bottom = r.bottom = 200; + + sciprintf("dx_draw_filled_rect(): %08X %i,%i -> %i,%i\n", lineColor, r.left, r.top, r.right, r.bottom); + + DODX( (dx_state->pTexVisuals[BACK_VIS]->LockRect(0, &lockedRect, &lr, 0)), dx_draw_filled_rect ); + UINT *rectPtr = (UINT*)lockedRect.pBits; + + // Going along x axis + for (int y_pixel = r.top; y_pixel < r.bottom; y_pixel++) + { + UINT *startLine = rectPtr; + for (int x_pixel = r.left; x_pixel < r.right; x_pixel++) + { + *rectPtr = lineColor; + rectPtr++; + } + rectPtr = startLine; + rectPtr += 320; + } + + DODX( (dx_state->pTexVisuals[BACK_VIS]->UnlockRect(0)), dx_draw_filled_rect ); + } + + if (color1.mask & GFX_MASK_PRIORITY) + { + byte *pri; + pri = dx_state->priority_maps[BACK_PRI]->index_data + box.x + box.y*(320); + for(int i=0; i<box.yl; i++) + { + memset(pri,color1.priority,box.xl); + pri += 320; + } + } + + return GFX_OK; +} + + +// Draws a single line to the back buffer +static int +dx_draw_line(struct _gfx_driver *drv, + point_t start, point_t end, + gfx_color_t color, + gfx_line_mode_t line_mode, gfx_line_style_t line_style) +{ + if (color.mask & GFX_MASK_VISUAL) { + + rect_t line_rect = { start.x, start.y, end.x - start.x + 1, end.y - start.y + 1 }; + + sciprintf("dx_draw_line(): %i,%i -> %i,%i\n", line_rect.x, line_rect.y, line_rect.xl + line_rect.x, line_rect.yl + line_rect.y); + + // Make sure priorities are not updated in dx_draw_filled_rect() + gfx_color_t col = color; + col.mask = GFX_MASK_VISUAL; + + dx_draw_filled_rect(drv, line_rect, col, col, GFX_SHADE_FLAT); + } + + if (color.mask & GFX_MASK_PRIORITY) { + gfx_draw_line_pixmap_i(dx_state->priority_maps[BACK_PRI], start, end, color.priority); + } + + return GFX_OK; +} + + +///// PIXMAPS + +// Register the pixmap as a texture +static int +dx_register_pixmap(struct _gfx_driver *drv, gfx_pixmap_t *pxm) +{ + HRESULT hr; + + int i, xs; + byte *s, *d; + D3DLOCKED_RECT lockedRect; + LPDIRECT3DTEXTURE8 newTex; + DODX( (dx_state->pDevice->CreateTexture(pxm->xl, pxm->yl, 1, 0, dx_state->d3dFormat, D3DPOOL_MANAGED, &newTex )), dx_register_pixmap ); + + // Do gfx crossblit + DODX( (newTex->LockRect(0, &lockedRect, NULL, 0)), dx_register_pixmap ); + s = pxm->data; + d = (byte *) lockedRect.pBits; + xs = drv->mode->bytespp * pxm->xl; + + for(i = 0; i < pxm->yl; i++) + { + memcpy(d, s, xs); + s += xs; + d += lockedRect.Pitch; + } + DODX( (newTex->UnlockRect(0)), dx_register_pixmap ); + + pxm->internal.info = newTex; + pxm->internal.handle = SCI_DX_HANDLE_NORMAL; + + return GFX_OK; +} + + +// Unregister the pixmap +static int +dx_unregister_pixmap(struct _gfx_driver *drv, gfx_pixmap_t *pxm) +{ + SAFE_RELEASE((LPDIRECT3DTEXTURE8) (pxm->internal.info)); + pxm->internal.info = NULL; + + return GFX_OK; +} + + +// Draws part of a pixmap to the static or back buffer +static int +dx_draw_pixmap(struct _gfx_driver *drv, gfx_pixmap_t *pxm, int priority, + rect_t src, rect_t dest, gfx_buffer_t buffer) +{ + HRESULT hr; + int bufnr = (buffer == GFX_BUFFER_STATIC) ? 2 : 1; + int pribufnr = bufnr - 1; + LPDIRECT3DTEXTURE8 srct, dstt; + LPDIRECT3DSURFACE8 sbuf, dbuf; + D3DLOCKED_RECT lockedRect; + byte *pri_map = NULL; + + if (pxm->internal.handle == SCI_DX_HANDLE_GRABBED) { + // copy from pxm->internal.info to visual[bufnr] + RECT srcRect = RECT_T_TO_RECT(src); + POINT dstPoint = { dest.x, dest.y }; + + srct = (LPDIRECT3DTEXTURE8) (pxm->internal.info); + dstt = dx_state->pTexVisuals[bufnr]; + + DODX( (srct->GetSurfaceLevel(0, &sbuf)), dx_draw_pixmap ); + DODX( (dstt->GetSurfaceLevel(0, &dbuf)), dx_draw_pixmap ); + DODX( (dx_state->pDevice->CopyRects(sbuf, &srcRect, 1, dbuf, &dstPoint)), dx_draw_pixmap ); + + SAFE_RELEASE(sbuf); + SAFE_RELEASE(dbuf); + + return GFX_OK; + } + + + // Create texture to temporarily hold visuals[bufnr] + LPDIRECT3DTEXTURE8 temp; + DODX( (dx_state->pDevice->CreateTexture(pxm->xl, pxm->yl, 1, 0, dx_state->d3dFormat, D3DPOOL_MANAGED, &temp)), dx_draw_pixmap ); + RECT srcRect = RECT_T_TO_RECT(dest); + RECT destRect = { 0, 0, dest.xl, dest.yl }; + POINT dstPoint = { destRect.left, destRect.top }; + + // Copy from visuals[bufnr] to temp + srct = dx_state->pTexVisuals[bufnr]; + dstt = temp; + DODX( (srct->GetSurfaceLevel(0, &sbuf)), dx_draw_pixmap ); + DODX( (dstt->GetSurfaceLevel(0, &dbuf)), dx_draw_pixmap ); + DODX( (dx_state->pDevice->CopyRects(sbuf, &srcRect, 1, dbuf, &dstPoint)), dx_draw_pixmap ); + + // Copy from given pixmap to temp + DODX( (dbuf->LockRect(&lockedRect, &destRect, 0)), dx_draw_pixmap ); + gfx_crossblit_pixmap(drv->mode, pxm, priority, src, dest, + (byte *) lockedRect.pBits, lockedRect.Pitch, + dx_state->priority_maps[pribufnr]->index_data, + dx_state->priority_maps[pribufnr]->index_xl, 1, + GFX_CROSSBLIT_FLAG_DATA_IS_HOMED); + DODX( (dbuf->UnlockRect()), dx_draw_pixmap ); + + SAFE_RELEASE(sbuf); + SAFE_RELEASE(dbuf); + + + // Copy from temp to visuals[bufnr] + RECT src2Rect = { 0, 0, dest.xl, dest.yl }; + POINT dst2Point = { dest.x, dest.y }; + + srct = temp; + dstt = dx_state->pTexVisuals[bufnr]; + DODX( (srct->GetSurfaceLevel(0, &sbuf)), dx_draw_pixmap ); + DODX( (dstt->GetSurfaceLevel(0, &dbuf)), dx_draw_pixmap ); + DODX( (dx_state->pDevice->CopyRects(sbuf, &src2Rect, 1, dbuf, &dst2Point)), dx_draw_pixmap ); + + SAFE_RELEASE(sbuf); + SAFE_RELEASE(dbuf); + SAFE_RELEASE(temp); + + return GFX_OK; +} + + +// Grabs an image from the visual or priority back buffer +static int +dx_grab_pixmap(struct _gfx_driver *drv, rect_t src, gfx_pixmap_t *pxm, + gfx_map_mask_t map) +{ + HRESULT hr; + + if (src.x < 0 || src.y < 0) { + GFXERROR("Attempt to grab pixmap from invalid coordinates (%d,%d)\n", src.x, src.y); + return GFX_ERROR; + } + + if (!pxm->data) { + GFXERROR("Attempt to grab pixmap to unallocated memory\n"); + return GFX_ERROR; + } + + // Choose map to grab from + switch (map) { + + case GFX_MASK_VISUAL: { + LPDIRECT3DTEXTURE8 temp; + LPDIRECT3DSURFACE8 tempSrf, backSrf; + CONST RECT srcRect = RECT_T_TO_RECT(src); + CONST POINT dstPoint = { 0, 0 }; + + pxm->xl = src.xl; + pxm->yl = src.yl; + + DODX( (dx_state->pDevice->CreateTexture(pxm->xl, pxm->yl, 1, 0, dx_state->d3dFormat, D3DPOOL_MANAGED, &temp)), dx_grab_pixmap ); + + DODX( (dx_state->pTexVisuals[BACK_VIS]->GetSurfaceLevel(0, &backSrf)), dx_grab_pixmap ); + DODX( (temp->GetSurfaceLevel(0, &tempSrf)), dx_grab_pixmap ); + DODX( (dx_state->pDevice->CopyRects(backSrf, &srcRect, 1, tempSrf, &dstPoint)), dx_grab_pixmap ); + + pxm->internal.info = temp; + pxm->internal.handle = SCI_DX_HANDLE_GRABBED; + pxm->flags |= GFX_PIXMAP_FLAG_INSTALLED | GFX_PIXMAP_FLAG_EXTERNAL_PALETTE | GFX_PIXMAP_FLAG_PALETTE_SET; + + SAFE_RELEASE(backSrf); + SAFE_RELEASE(tempSrf); + + break; + } + + case GFX_MASK_PRIORITY: + sciprintf("FIXME: priority map grab not implemented yet!\n"); + break; + + default: + sciprintf("Attempt to grab pixmap from invalid map 0x%02x\n", map); + return GFX_ERROR; + } + + return GFX_OK; +} + + +///// BUFFERING + +// Updates the front buffer or the back buffers +static int +dx_update(struct _gfx_driver *drv, rect_t src, point_t dest, gfx_buffer_t buffer) +{ + HRESULT hr; + LPDIRECT3DTEXTURE8 srct, dstt; + LPDIRECT3DSURFACE8 sbuf, dbuf; + CONST RECT srcRect = RECT_T_TO_RECT(src); + CONST POINT dstPoint = { dest.x, dest.y }; + + switch (buffer) { + + case GFX_BUFFER_FRONT: + srct = dx_state->pTexVisuals[BACK_VIS]; + dstt = dx_state->pTexVisuals[PRIMARY_VIS]; + + DODX( (srct->GetSurfaceLevel(0, &sbuf)), dx_update ); + DODX( (dstt->GetSurfaceLevel(0, &dbuf)), dx_update ); + + DODX( (dx_state->pDevice->CopyRects(sbuf, &srcRect, 1, dbuf, &dstPoint)), dx_update ); + + SAFE_RELEASE(sbuf); + SAFE_RELEASE(dbuf); + + RenderD3D(drv); + break; + + case GFX_BUFFER_BACK: + if (src.x == dest.x && src.y == dest.y) + gfx_copy_pixmap_box_i(dx_state->priority_maps[BACK_PRI], dx_state->priority_maps[STATIC_PRI], src); + + srct = dx_state->pTexVisuals[STATIC_VIS]; + dstt = dx_state->pTexVisuals[BACK_VIS]; + + DODX( (srct->GetSurfaceLevel(0, &sbuf)), dx_update ); + DODX( (dstt->GetSurfaceLevel(0, &dbuf)), dx_update ); + + DODX( (dx_state->pDevice->CopyRects(sbuf, &srcRect, 1, dbuf, &dstPoint)), dx_update ); + + SAFE_RELEASE(sbuf); + SAFE_RELEASE(dbuf); + + break; + + default: + GFXERROR("Invalid buffer %d in update!\n", buffer); + return GFX_ERROR; + } + + return GFX_OK; +} + + +// Sets the contents of the static visual and priority buffers +static int +dx_set_static_buffer(struct _gfx_driver *drv, gfx_pixmap_t *pic, + gfx_pixmap_t *priority) +{ + if (!pic->internal.info) { + GFXERROR("Attempt to set static buffer with unregistered pixmap!\n"); + return GFX_ERROR; + } + + HRESULT hr; + LPDIRECT3DTEXTURE8 pii = (LPDIRECT3DTEXTURE8) (pic->internal.info); + LPDIRECT3DSURFACE8 pbf; + LPDIRECT3DTEXTURE8 vis = dx_state->pTexVisuals[STATIC_VIS]; + LPDIRECT3DSURFACE8 vbf; + + // Copy from pic to visual[static] + DODX( (pii->GetSurfaceLevel(0, &pbf)), dx_set_static_buffer ); + DODX( (vis->GetSurfaceLevel(0, &vbf)), dx_set_static_buffer ); + DODX( (dx_state->pDevice->CopyRects(pbf, NULL, 0, vbf, NULL)), dx_set_static_buffer ); + + SAFE_RELEASE(pbf); + SAFE_RELEASE(vbf); + + // Copy priority map + gfx_copy_pixmap_box_i(dx_state->priority_maps[STATIC_PRI], priority, gfx_rect(0, 0, 320, 200)); + + return GFX_OK; +} + + +///// MOUSE + +// Sets a new mouse pointer +static int +dx_set_pointer(struct _gfx_driver *drv, gfx_pixmap_t *pointer) +{ + HRESULT hr; + + if (pointer->data == NULL) + return GFX_OK; + pointer->yl /= 2; // Scale the pointer to compensate for mode scale + + LPDIRECT3DTEXTURE8 pntTex; + LPDIRECT3DSURFACE8 pntSrf; + + // Get pointer dimensions and init + POINTS pDims = {pointer->xl, pointer->yl}; + dx_state->pointerDims = pDims; + RECT r = {0, 0, pointer->xl, pointer->yl}; + + // Recreate pointer data according to the graphics mode we need + gfx_pixmap_free_data(pointer); + gfx_xlate_pixmap(pointer, dx_state->pointerMode, GFX_XLATE_FILTER_NONE); + + // Create texture and fill with pointer data + DODX( (dx_state->pDevice->CreateTexture(pointer->xl, pointer->yl, 1, 0, dx_state->d3dFormat, D3DPOOL_MANAGED, &pntTex )), dx_set_pointer ); + DODX( (pntTex->GetSurfaceLevel(0, &pntSrf)), dx_set_pointer ); + + DODX( (D3DXLoadSurfaceFromMemory(pntSrf, NULL, &r, pointer->data, dx_state->d3dFormat, 256, NULL, &r, D3DX_FILTER_NONE, 0)), dx_set_pointer); + + SAFE_RELEASE(pntSrf); + + // Assign as current pointer texture + if (dx_state->pTexPointer) + SAFE_RELEASE(dx_state->pTexPointer); + dx_state->pTexPointer = pntTex; + + return GFX_OK; +} + + +// Display mouse pointer +static int +show_pointer(struct _gfx_driver *drv, LPARAM pos) +{ + POINTS mousePos; // mouse coordinates + + // Sort out coordinates + mousePos = MAKEPOINTS(pos); + + // Update pos + drv->pointer_x = mousePos.x; + drv->pointer_y = mousePos.y; + + RenderD3D(drv); + + return GFX_OK; +} + + +///// EVENTS + +// Get event from the queue +static sci_event_t +get_queue_event(gfx_dx_struct_t *ctx) +{ + if (ctx->queue_first == ctx->queue_size) + ctx->queue_first = 0; + + if (ctx->queue_first == ctx->queue_last) + { + sci_event_t noevent; + noevent.data = 0; + noevent.type = SCI_EVT_NONE; + noevent.buckybits = 0; + return noevent; + } + else + return ctx->event_queue [ctx->queue_first++]; +} + + +// Add event to the queue +static void add_queue_event(gfx_dx_struct_t *ctx, int type, int data, short buckybits) +{ + if ((ctx->queue_last+1) % ctx->queue_size == ctx->queue_first) + { + /* Reallocate queue */ + int i, event_count; + sci_event_t *new_queue; + + new_queue = (sci_event_t *) sci_malloc (ctx->queue_size * 2 * sizeof (sci_event_t)); + event_count = (ctx->queue_last - ctx->queue_first) % ctx->queue_size; + for (i=0; i<event_count; i++) + new_queue [i] = ctx->event_queue [(ctx->queue_first+i) % ctx->queue_size]; + free (ctx->event_queue); + ctx->event_queue = new_queue; + ctx->queue_size *= 2; + ctx->queue_first = 0; + ctx->queue_last = event_count; + } + + ctx->event_queue [ctx->queue_last].data = data; + ctx->event_queue [ctx->queue_last].type = type; + ctx->event_queue [ctx->queue_last++].buckybits = buckybits; + if (ctx->queue_last == ctx->queue_size) + ctx->queue_last=0; +} + + +// Add keystroke event to queue +static void add_key_event (gfx_dx_struct_t *ctx, int data) +{ + short buckybits = 0; + + if (GetAsyncKeyState (VK_RSHIFT)) + buckybits |= SCI_EVM_RSHIFT; + if (GetAsyncKeyState (VK_LSHIFT)) + buckybits |= SCI_EVM_LSHIFT; + if (GetAsyncKeyState (VK_CONTROL)) + buckybits |= SCI_EVM_CTRL; + if (GetAsyncKeyState (VK_MENU)) + buckybits |= SCI_EVM_ALT; + if (GetKeyState (VK_SCROLL) & 1) + buckybits |= SCI_EVM_SCRLOCK; + if (GetKeyState (VK_NUMLOCK) & 1) + buckybits |= SCI_EVM_NUMLOCK; + if (GetKeyState (VK_CAPITAL) & 1) + buckybits |= SCI_EVM_CAPSLOCK; + + add_queue_event (ctx, SCI_EVT_KEYBOARD, data, buckybits); +} + + +// Add mouse event to queue +static void add_mouse_event(gfx_dx_struct_t *ctx, int type, int data, WPARAM wParam) +{ + short buckybits = 0; + if (wParam & MK_SHIFT) + buckybits |= SCI_EVM_LSHIFT | SCI_EVM_RSHIFT; + if (wParam & MK_CONTROL) + buckybits |= SCI_EVM_CTRL; + + add_queue_event (ctx, type, data, buckybits); +} + + +// Returns the next event in the event queue for this driver +static sci_event_t +dx_get_event(struct _gfx_driver *drv) +{ + assert(drv->state != NULL); + return get_queue_event(dx_state); +} + + +// Sleeps the specified amount of microseconds, or until the mouse moves +static int +dx_usleep(struct _gfx_driver *drv, long usecs) +{ + if (usecs < 1000) + { + sleep(0); + } + else + { + sleep(usecs/1000); + } + ProcessMessages(drv); + + return GFX_OK; +} + + +// Process any Windows messages +static int +ProcessMessages(struct _gfx_driver *drv) +{ + MSG msg; + + while (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE)) { + switch( msg.message ) + { + case WM_PAINT: + ValidateRect( dx_state->hWnd, NULL ); + RenderD3D(drv); + break; + + case WM_KEYDOWN: + switch (msg.wParam) + { + MAP_KEY (VK_ESCAPE, SCI_K_ESC); + MAP_KEY (VK_BACK, SCI_K_BACKSPACE); + MAP_KEY (VK_RETURN, SCI_K_ENTER); + MAP_KEY (VK_TAB, SCI_K_TAB); + MAP_KEY (VK_END, SCI_K_END); + MAP_KEY (VK_DOWN, SCI_K_DOWN); + MAP_KEY (VK_NEXT, SCI_K_PGDOWN); + MAP_KEY (VK_LEFT, SCI_K_LEFT); + MAP_KEY (VK_RIGHT, SCI_K_RIGHT); + MAP_KEY (VK_HOME, SCI_K_HOME); + MAP_KEY (VK_UP, SCI_K_UP); + MAP_KEY (VK_PRIOR, SCI_K_PGUP); + MAP_KEY (VK_INSERT, SCI_K_INSERT); + MAP_KEY (VK_DELETE, SCI_K_DELETE); + MAP_KEY (VK_DECIMAL, SCI_K_DELETE); + /* TODO: Glutton no longer had SCI_K etc... declared + MAP_KEY (VK_ADD, SCI_K_PLUS); + MAP_KEY (VK_OEM_PLUS, SCI_K_EQUALS); + MAP_KEY (VK_SUBTRACT, SCI_K_MINUS); + MAP_KEY (VK_OEM_MINUS, SCI_K_MINUS); + MAP_KEY (VK_MULTIPLY, SCI_K_MULTIPLY); + MAP_KEY (VK_DIVIDE, SCI_K_DIVIDE); + */ + MAP_KEY (VK_OEM_COMMA, ','); + MAP_KEY (VK_OEM_PERIOD, '.'); + MAP_KEY (VK_OEM_1, ';'); // US keyboards only + MAP_KEY (VK_OEM_2, '/'); + MAP_KEY (VK_OEM_3, '`'); + MAP_KEY (VK_OEM_4, '['); + MAP_KEY (VK_OEM_5, '\\'); + MAP_KEY (VK_OEM_6, ']'); + MAP_KEY (VK_OEM_7, '\''); + MAP_KEY (VK_F1, SCI_K_F1); + MAP_KEY (VK_F2, SCI_K_F2); + MAP_KEY (VK_F3, SCI_K_F3); + MAP_KEY (VK_F4, SCI_K_F4); + MAP_KEY (VK_F5, SCI_K_F5); + MAP_KEY (VK_F6, SCI_K_F6); + MAP_KEY (VK_F7, SCI_K_F7); + MAP_KEY (VK_F8, SCI_K_F8); + MAP_KEY (VK_F9, SCI_K_F9); + MAP_KEY (VK_F10, SCI_K_F10); + + case VK_RSHIFT: + case VK_LSHIFT: + case VK_CONTROL: + case VK_MENU: + case VK_SCROLL: + case VK_NUMLOCK: + case VK_CAPITAL: + break; // ignore + + default: + if (msg.wParam >= 'A' && msg.wParam <= 'Z') + add_key_event (dx_state, msg.wParam - 'A' + 97); + else if (msg.wParam >= VK_NUMPAD0 && msg.wParam <= VK_NUMPAD9) + { + if (GetKeyState (VK_NUMLOCK) & 1) + add_key_event (dx_state, msg.wParam - VK_NUMPAD0 + '0'); + else + switch (msg.wParam) + { + MAP_KEY (VK_NUMPAD0, SCI_K_INSERT); + MAP_KEY (VK_NUMPAD1, SCI_K_END); + MAP_KEY (VK_NUMPAD2, SCI_K_DOWN); + MAP_KEY (VK_NUMPAD3, SCI_K_PGDOWN); + MAP_KEY (VK_NUMPAD4, SCI_K_LEFT); + MAP_KEY (VK_NUMPAD6, SCI_K_RIGHT); + MAP_KEY (VK_NUMPAD7, SCI_K_HOME); + MAP_KEY (VK_NUMPAD8, SCI_K_UP); + MAP_KEY (VK_NUMPAD9, SCI_K_PGUP); + } + } + else if (msg.wParam == 0xC0) // tilde key - used for invoking console + add_key_event (dx_state, '`'); + else + add_key_event (dx_state, msg.wParam); + break; + } + break; + + case WM_MOUSEMOVE: + // Turn off mouse cursor + ShowCursor(FALSE); + show_pointer(drv, msg.lParam); + break; + + case WM_MOUSELEAVE: + // Turn on mouse cursor + ShowCursor(TRUE); + break; + + case WM_LBUTTONDOWN: add_mouse_event (dx_state, SCI_EVT_MOUSE_PRESS, 1, msg.wParam); break; + case WM_RBUTTONDOWN: add_mouse_event (dx_state, SCI_EVT_MOUSE_PRESS, 2, msg.wParam); break; + case WM_MBUTTONDOWN: add_mouse_event (dx_state, SCI_EVT_MOUSE_PRESS, 3, msg.wParam); break; + case WM_LBUTTONUP: add_mouse_event (dx_state, SCI_EVT_MOUSE_RELEASE, 1, msg.wParam); break; + case WM_RBUTTONUP: add_mouse_event (dx_state, SCI_EVT_MOUSE_RELEASE, 2, msg.wParam); break; + case WM_MBUTTONUP: add_mouse_event (dx_state, SCI_EVT_MOUSE_RELEASE, 3, msg.wParam); break; + + case WM_DESTROY: + PostQuitMessage( 0 ); + drv->exit(drv); + exit(-1); + break; + + } + } + + return 0; +} + + +extern "C" +gfx_driver_t gfx_driver_dx = { + "directx", + "0.4.2", + SCI_GFX_DRIVER_MAGIC, + SCI_GFX_DRIVER_VERSION, + NULL, /* mode */ + 0, 0, /* mouse pointer position */ + GFX_CAPABILITY_MOUSE_SUPPORT | GFX_CAPABILITY_MOUSE_POINTER | GFX_CAPABILITY_COLOR_MOUSE_POINTER | GFX_CAPABILITY_PIXMAP_REGISTRY | GFX_CAPABILITY_WINDOWED, + 0, + dx_set_param, + dx_init_specific, + dx_init, + dx_exit, + dx_draw_line, + dx_draw_filled_rect, + dx_register_pixmap, + dx_unregister_pixmap, + dx_draw_pixmap, + dx_grab_pixmap, + dx_update, + dx_set_static_buffer, + dx_set_pointer, + NULL, + dx_get_event, + dx_usleep, + NULL +}; + +#endif diff --git a/engines/sci/gfx/drivers/dx_driver.h b/engines/sci/gfx/drivers/dx_driver.h new file mode 100644 index 0000000000..3eb280cefb --- /dev/null +++ b/engines/sci/gfx/drivers/dx_driver.h @@ -0,0 +1,151 @@ +/*************************************************************************** + graphics_directx.h Copyright (C) 2008 Alexander R Angas, + Some portions Copyright (C) 1999 Dmitry Jemerov + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Alexander R Angas (Alex) <arangas AT internode dot on dot net> + +***************************************************************************/ + +#include <windows.h> +#include <d3d8.h> +#include <d3dx8math.h> +#include <dxerr8.h> + +extern "C" { +#include <gfx_system.h> +#include <gfx_driver.h> +#include <gfx_tools.h> +#include <assert.h> +#include <uinput.h> +#include <ctype.h> +#include <console.h> // for sciprintf +#include <sci_win32.h> +#include <sci_memory.h> +}; + +// Error trapping, every DirectX call should use this +#define DODX(cmd, proc) \ + hr = cmd; \ + if (hr != S_OK) { \ + sciprintf("%s, %i, %i, %s from %s\n", __FILE__, __LINE__, hr, #cmd, #proc); \ + DXTrace(__FILE__, __LINE__, hr, #cmd" from "#proc, 1); \ + } + + +// Easily release only allocated objects +#define SAFE_RELEASE(p) \ + if (p) \ + (p)->Release(); + + +// Make it simple to access drv->state +#define dx_state ((struct gfx_dx_struct_t *)(drv->state)) + + +// Simply map a key using add_key_event() +#define MAP_KEY(x,y) case x: add_key_event ((struct gfx_dx_struct_t *)(drv->state), y); break + + +#define DX_CLASS_NAME "FreeSCI DirectX Graphics" +#define DX_APP_NAME "FreeSCI" + +// Vertex format +#define D3DFVF_CUSTOMVERTEX ( D3DFVF_DIFFUSE | D3DFVF_XYZRHW | D3DFVF_TEX1 ) + +// Vertex structure +struct CUSTOMVERTEX +{ + D3DXVECTOR4 p; // Vertex coordinates + DWORD colour; // Colour + D3DXVECTOR2 t; // Texture coordinates +}; + +#define SCI_DX_HANDLE_NORMAL 0 +#define SCI_DX_HANDLE_GRABBED 1 + +// Number of buffers for each type of texture +#define NUM_VISUAL_BUFFERS 3 +#define NUM_PRIORITY_BUFFERS 2 + +// What each buffer references +#define PRIMARY_VIS 0 +#define BACK_VIS 1 +#define STATIC_VIS 2 + +#define BACK_PRI 0 +#define STATIC_PRI 1 + +// Struct that holds everything +struct gfx_dx_struct_t +{ + D3DFORMAT d3dFormat; // Colour format + UINT adapterId; // Adapter ID to use + DWORD vertexProcessing; // Hardware or software vertex processing + + LPDIRECT3D8 pD3d; // D3D object + D3DCAPS8 deviceCaps; // Capabilities of device + D3DDISPLAYMODE displayMode; // Width and height of screen + D3DPRESENT_PARAMETERS presParams; // Presentation parameters + LPDIRECT3DDEVICE8 pDevice; // Rendering device + + LPDIRECT3DVERTEXBUFFER8 pVertBuff; // Buffer to hold pixmap vertices + CUSTOMVERTEX pvData[4]; // Buffer of pixmap vertex structs + + LPDIRECT3DTEXTURE8 pTexVisuals[NUM_VISUAL_BUFFERS]; // Array of visual textures + LPDIRECT3DTEXTURE8 pTexPrioritys[NUM_PRIORITY_BUFFERS]; // Array of priority textures + gfx_pixmap_t *priority_maps[NUM_PRIORITY_BUFFERS]; // Array of SCI priority maps + + gfx_mode_t *pointerMode; // SCI graphics mode for pointer + LPDIRECT3DTEXTURE8 pTexPointer; // Mouse pointer texture + LPD3DXSPRITE pSPointer; // Mouse pointer sprite + POINTS pointerDims; // Pointer dimensions + + WNDCLASSEX wc; // Window class + HWND hWnd; // Window + UINT xfact, yfact; // Scaling factors + UINT bpp; // Bits per pixel + + // Event queue + int queue_size, queue_first, queue_last; + sci_event_t *event_queue; +}; + +// Flags that may be set in the driver +#define DX_FLAGS_FULLSCREEN 1 + +// Initialization functions +static int +ProcessMessages(struct _gfx_driver *drv); + +static gfx_return_value_t +RenderD3D(struct _gfx_driver *drv); + +static int +CheckDevice(struct _gfx_driver *drv); + +static gfx_return_value_t +InitWindow(struct _gfx_driver *drv, UINT width, UINT height); + +static gfx_return_value_t +InitD3D(struct _gfx_driver *drv); + +static gfx_return_value_t +InitScene(struct _gfx_driver *drv); diff --git a/engines/sci/gfx/drivers/gfx_drivers.c b/engines/sci/gfx/drivers/gfx_drivers.c new file mode 100644 index 0000000000..b47d6467c9 --- /dev/null +++ b/engines/sci/gfx/drivers/gfx_drivers.c @@ -0,0 +1,192 @@ +/*************************************************************************** + gfx_drivers.c Copyright (C) 2001 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) <jameson@linuxgames.com> + +***************************************************************************/ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif +#include <gfx_driver.h> +#include <modules.h> + + +static char *oldname = NULL; +static void *oldhandle; + + + +#ifndef HAVE_DLOPEN +# ifdef HAVE_LIBGGI +extern gfx_driver_t gfx_driver_ggi; +# endif + + +# ifndef X_DISPLAY_MISSING +extern gfx_driver_t gfx_driver_xlib; +# endif + +# ifdef HAVE_DIRECTX +extern gfx_driver_t gfx_driver_dx; +# endif + +# ifdef HAVE_DDRAW +extern gfx_driver_t gfx_driver_dd; +# endif + +# ifdef HAVE_SDL +extern gfx_driver_t gfx_driver_sdl; +# endif + +# ifdef HAVE_DIRECTFB +extern gfx_driver_t gfx_driver_dfb; +# endif + +# ifdef _DREAMCAST +extern gfx_driver_t gfx_driver_dc; +# endif +#endif + +extern gfx_driver_t gfx_driver_null; + +static gfx_driver_t *gfx_drivers[] = { +#ifndef HAVE_DLOPEN +# ifndef X_DISPLAY_MISSING + &gfx_driver_xlib, +# endif +# ifdef HAVE_SDL + &gfx_driver_sdl, +# endif +# ifdef HAVE_DIRECTX + &gfx_driver_dx, +# endif +# ifdef HAVE_DDRAW + &gfx_driver_dd, +# endif +# ifdef HAVE_DIRECTFB + &gfx_driver_dfb, +# endif +# ifdef HAVE_LIBGGI + &gfx_driver_ggi, +# endif +# ifdef _DREAMCAST + &gfx_driver_dc, +# endif +#endif + &gfx_driver_null, + NULL +}; + +#define DRIVER_TYPE "gfx" +#define DRIVER_PREFIX "gfx_driver_" +#define DRIVER_FILE_SUFFIX "_driver" + +#ifdef HAVE_DLOPEN +struct _gfx_driver * +gfx_find_driver(char *path, char *name) +{ + int retval = 0; + + if (oldhandle) + sci_close_module(oldhandle, DRIVER_TYPE, oldname); + + if (!name) { /* Find default driver */ +#ifdef _WIN32 + name = "sdl"; +#else /* !_WIN32 */ +# ifndef X_DISPLAY_MISSING + if (getenv("DISPLAY")) + name = "xlib"; + else +# endif + name = "ggi"; +#endif /* !_WIN32 */ + } + + oldname = name; + return (struct _gfx_driver *) + sci_find_module(path, name, DRIVER_TYPE, + DRIVER_PREFIX, + DRIVER_FILE_SUFFIX, + SCI_GFX_DRIVER_MAGIC, + SCI_GFX_DRIVER_VERSION, + &oldhandle); +} +#else /* No dlopen */ +struct _gfx_driver * +gfx_find_driver(char *path, char *name) +{ + int retval = 0; + + if (!name) { /* Find default driver */ +#ifndef X_DISPLAY_MISSING + if (getenv("DISPLAY")) + return &gfx_driver_xlib; +#endif +#if defined (MACOSX) && defined(HAVE_SDL) + return &gfx_driver_sdl; +#endif + return gfx_drivers[0]; + } + + while (gfx_drivers[retval] && strcasecmp(name, gfx_drivers[retval]->name)) + retval++; + + return gfx_drivers[retval]; +} +#endif + + +const char * +gfx_get_driver_name(int nr) +{ + return (gfx_drivers[nr])? gfx_drivers[nr]->name : NULL; +} + + +int +string_truep(char *value) +{ + return !(strcasecmp(value, "ok") && + strcasecmp(value, "enable") && + strcasecmp(value, "1") && + strcasecmp(value, "true") && + strcasecmp(value, "yes") && + strcasecmp(value, "on")); +} + + +int +string_falsep(char *value) +{ + return !(strcasecmp(value, "disable") && + strcasecmp(value, "0") && + strcasecmp(value, "false") && + strcasecmp(value, "no") && + strcasecmp(value, "off")); +} + + + + diff --git a/engines/sci/gfx/drivers/ggi_driver.c b/engines/sci/gfx/drivers/ggi_driver.c new file mode 100644 index 0000000000..c6cf4b0f00 --- /dev/null +++ b/engines/sci/gfx/drivers/ggi_driver.c @@ -0,0 +1,1035 @@ +/*************************************************************************** + ggi_driver.c Copyright (C) 2000 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) <jameson@linuxgames.com> + +***************************************************************************/ +/* FreeSCI 0.3.1+ graphics driver module for libggi */ + + +#include <stdarg.h> +#include <gfx_system.h> +#include <gfx_driver.h> +#include <gfx_tools.h> +#include <assert.h> +#include <uinput.h> + +#ifdef HAVE_LIBGGI + +#include <ggi/ggi.h> +#include <ctype.h> + + +#define GGI_DRIVER_VERSION "0.4" + +#define GFX_GGI_DEBUG + +#ifdef GFX_GGI_DEBUG + +#define POSMSG sciprintf("%s L%d:", __FILE__, __LINE__) +#define DEBUG_POINTER (!(drv->debug_flags & GFX_DEBUG_POINTER))? 0 : POSMSG && gfxprintf +#define DEBUG_UPDATES (!(drv->debug_flags & GFX_DEBUG_UPDATES))? 0 : POSMSG && gfxprintf +#define DEBUG_PIXMAPS (!(drv->debug_flags & GFX_DEBUG_PIXMAPS))? 0 : POSMSG && sciprintf +#define DEBUG_BASIC (!(drv->debug_flags & GFX_DEBUG_BASIC))? 0 : POSMSG && sciprintf +#else /* !GFX_GGI_DEBUG */ +#define DEBUG_POINTER (1)? 0 : +#define DEBUG_UPDATES (1)? 0 : +#define DEBUG_PIXMAPS (1)? 0 : +#define DEBUG_BASIC (1)? 0 : +#endif /* !GFX_GGI_DEBUG */ + +static gfx_mode_t * +_aberr(char *file, int line, char *message); + +static void +init_input_ggi(); + +#define MODE ((drv->mode)? drv->mode : _aberr(__FILE__, __LINE__, "drv->mode is NULL")) +#define STATE ((gfx_ggi_struct_t *)drv->state) +#define VISUAL ((gfx_ggi_struct_t *)drv->state)->vis +#define FRAMES ((gfx_ggi_struct_t *)drv->state)->frames + +static gfx_mode_t * +_aberr(char *file, int line, char *message) +{ + fprintf(stderr,"GFXGGI: Fatal: %s L%d: %s\n", file, line, message); + exit(1); /* Die */ + return NULL; +} + +#define GGI_BUFFER_BACK 0 +#define GGI_BUFFER_STATIC 1 + +#define SCI_GGI_SWAP_CTRL_CAPS (1 << 0) + +typedef struct { + ggi_visual_t vis; + int frames; + + byte *alt_back_buffer; /* if frames < 2: Virtual back buffer */ + ggi_visual_t back_vis; /* Memory visual for the back buffer */ + + byte *static_buffer; /* if frames < 3: Virtual static buffer */ + ggi_visual_t static_vis; /* Memory visual for the static buffer */ + + gfx_pixmap_t *priority_maps[2]; + ggi_visual_t priority_visuals[2]; /* Visuals for the maps */ + + int x_blank, y_blank; + int x_blank2, y_blank2; + +} gfx_ggi_struct_t; + +static int flags; + +static int +ggi_set_param(gfx_driver_t *drv, char *attribute, char *value) +{ + gfx_ggi_struct_t *meta = (gfx_ggi_struct_t *) drv->state; + + if (!strcmp(attribute, "swap_ctrl_caps") || + !strcmp(attribute, "swap_caps_ctrl")) { + if (string_truep(value)) + flags |= SCI_GGI_SWAP_CTRL_CAPS; + else + flags &= ~SCI_GGI_SWAP_CTRL_CAPS; + + return GFX_OK; + } + + DEBUG_BASIC("ggi_set_param('%s' to '%s')\n", attribute, value); + return GFX_ERROR; +} + + +static int +_open_meta_visuals(gfx_driver_t *drv) +{ + int i; + + for (i = 0; i < 2; i++) { + if (!(STATE->priority_visuals[i] = ggiOpen("memory:pointer", STATE->priority_maps[i]->data))) { + sciprintf("GFXGGI: Could not open priority map #%d\n", i); + return 1; + } + if (ggiSetSimpleMode(STATE->priority_visuals[i], 320 * MODE->xfact, 200 * MODE->yfact, 1, GT_8BIT)) { + sciprintf("GFXGGI: Could not set mode for priority visual %d\n", i); + return 1; + } + } + + return 0; +} + +static int +ggi_init_specific(gfx_driver_t *drv, int xres, int yres, int bpp) +{ + gfx_ggi_struct_t *meta; + ggi_graphtype graphtype; + const ggi_pixelformat *pixelformat; + int frames = 3; + + switch (bpp) { + case 1: graphtype = GT_8BIT; break; + case 2: graphtype = GT_16BIT; break; + case 3: graphtype = GT_24BIT; break; + case 4: graphtype = GT_32BIT; break; + default: sciprintf("GFXGGI: Error: Invalid bytes per pixel value: %d\n", bpp); + return GFX_ERROR; + } + + drv->state = NULL; + + if (ggiInit() < 0) + return GFX_FATAL; + + meta = (gfx_ggi_struct_t *) sci_calloc(sizeof(gfx_ggi_struct_t), 1); + + if (!(meta->vis = ggiOpen(NULL))) { + DEBUG_BASIC("ggiOpen() failed!\n"); + free(meta); + ggiExit(); + return GFX_FATAL; + } + + while ((frames > 0) && (ggiSetSimpleMode(meta->vis, xres * 320, yres * 200, frames, graphtype))) + --frames; + + if (!frames) { + DEBUG_BASIC("Initializing %dx%d at %d bpp failed\n", xres*320, yres*200, bpp << 3); + free(meta); + ggiExit(); + return GFX_ERROR; + } + + DEBUG_BASIC("Initialized with %d frames\n", frames); + + meta->frames = frames; + + pixelformat = ggiGetPixelFormat(meta->vis); + drv->mode = gfx_new_mode(xres, yres, pixelformat->size >> 3, + pixelformat->red_mask, pixelformat->green_mask, pixelformat->blue_mask, + 0, /* alpha mask */ + pixelformat->red_shift, pixelformat->green_shift, pixelformat->blue_shift, + 0, /* alpha shift */ + (bpp == 1)? 256 : 0, 0); + drv->state = meta; + + meta->priority_maps[GGI_BUFFER_BACK] = + gfx_pixmap_alloc_index_data(gfx_new_pixmap(320 * xres, 200 * yres, GFX_RESID_NONE, 0, 0)); + meta->priority_maps[GGI_BUFFER_STATIC] = + gfx_pixmap_alloc_index_data(gfx_new_pixmap(320 * xres, 200 * yres, GFX_RESID_NONE, 0, 0)); + + meta->priority_maps[GGI_BUFFER_BACK]->flags |= GFX_PIXMAP_FLAG_SCALED_INDEX; + meta->priority_maps[GGI_BUFFER_STATIC]->flags |= GFX_PIXMAP_FLAG_SCALED_INDEX; + + if (_open_meta_visuals(drv)) { + free(meta); + gfx_free_pixmap(drv, meta->priority_maps[GGI_BUFFER_BACK]); + gfx_free_pixmap(drv, meta->priority_maps[GGI_BUFFER_STATIC]); + ggiClose(meta->vis); + ggiExit(); + return GFX_ERROR; + } + + if (frames < 2) { + meta->alt_back_buffer = (byte *) sci_malloc(bpp * 320 * 200 * xres * yres); + meta->back_vis = ggiOpen("memory:pointer", meta->alt_back_buffer, NULL); + if (ggiSetSimpleMode(meta->back_vis, xres * 320, yres * 200, 1, GT_8BIT)) { + sciprintf("GFXGGI: Warning: Setting mode for memory visual failed\n"); + } + } else meta->alt_back_buffer = NULL; + + if (frames < 3) { + meta->static_buffer = (byte *) sci_malloc(bpp * 320 * 200 * xres * yres); + meta->static_vis = ggiOpen("memory:pointer", meta->static_buffer, NULL); + if (ggiSetSimpleMode(meta->static_vis, xres * 320, yres * 200, 1, GT_8BIT)) { + sciprintf("GFXGGI: Warning: Setting mode for memory visual #2 failed\n"); + } + } else meta->static_buffer = NULL; + + init_input_ggi(); + flags = 0; + + return GFX_OK; +} + + +static int +ggi_init(gfx_driver_t *drv) +{ + gfx_ggi_struct_t *meta; + ggi_mode mode; + int x_blank, y_blank; + const ggi_pixelformat *pixelformat; + mode.frames = 3; + mode.visible.x = mode.visible.y = mode.virt.x = mode.virt.y + = mode.size.x = mode.size.y = mode.dpp.x = mode.dpp.y = GGI_AUTO; + + mode.graphtype = GT_AUTO; + + drv->state = NULL; + + if (ggiInit() < 0) + return GFX_FATAL; + + meta = (gfx_ggi_struct_t *) sci_calloc(sizeof(gfx_ggi_struct_t), 1); + + if (!(meta->vis = ggiOpen(NULL))) { + DEBUG_BASIC("ggiOpen() failed!\n"); + free(meta); + ggiExit(); + return GFX_FATAL; + } + + while (mode.frames && ggiCheckMode(meta->vis, &mode)) + --(mode.frames); + + if (!mode.frames) { + sciprintf("GFXGGI: Could not find any graphics mode!\n"); + free(meta); + ggiExit(); + return GFX_FATAL; + } + + x_blank = mode.visible.x % 320; + y_blank = mode.visible.y % 200; + mode.visible.x -= x_blank; + mode.visible.y -= y_blank; + + if (mode.visible.x == 0) + mode.visible.x = 320; + + if (mode.visible.y == 0) + mode.visible.y = 200; + + if (GT_DEPTH(mode.graphtype) < 8) + mode.graphtype = GT_8BIT; /* We don't support less than 8 bpp */ + + DEBUG_BASIC("Attempting to create mode with %dx%d, graphtype=%08x, %d frames\n", + mode.visible.x, mode.visible.y, mode.graphtype, mode.frames); + + mode.virt.x = mode.visible.x; + mode.virt.y = mode.visible.y; + mode.size.x = GGI_AUTO; + mode.size.y = GGI_AUTO; + + if (ggiSetMode(meta->vis, &mode)) { + sciprintf("GFXGGI: Could not set proposed graphics mode!\n"); + + mode.virt.x = mode.visible.x += x_blank; + mode.virt.y = mode.visible.y += y_blank; + mode.size.x = GGI_AUTO; + mode.size.y = GGI_AUTO; + + DEBUG_BASIC("Attempting to create augmented mode with %dx%d, graphtype=%08x, %d frames\n", + mode.visible.x, mode.visible.y, mode.graphtype, mode.frames); + + if (ggiSetMode(meta->vis, &mode)) { + sciprintf("GFXGGI: Could not set proposed graphics mode!\n"); + free(meta); + ggiExit(); + return GFX_FATAL; + } + + ggiSetOrigin(meta->vis, (x_blank >> 1), (y_blank >> 1)); + + mode.virt.x = mode.size.x = mode.visible.x -= x_blank; + mode.virt.y = mode.size.y = mode.visible.y -= y_blank; + } else + x_blank = y_blank = 0; + + meta->frames = mode.frames; + + pixelformat = ggiGetPixelFormat(meta->vis); + + drv->mode = gfx_new_mode(mode.visible.x / 320, mode.visible.y / 200, pixelformat->size >> 3, + pixelformat->red_mask, pixelformat->green_mask, pixelformat->blue_mask, + 0, /* alpha mask */ + pixelformat->red_shift, pixelformat->green_shift, pixelformat->blue_shift, + 0, /* alpha shift */ + (GT_SCHEME(mode.graphtype) == GT_PALETTE)? (1 << GT_DEPTH(mode.graphtype)) : 0, 0); + + drv->state = meta; + + meta->priority_maps[GGI_BUFFER_BACK] = + gfx_pixmap_alloc_index_data(gfx_new_pixmap(mode.visible.x, mode.visible.y, GFX_RESID_NONE, 0, 0)); + meta->priority_maps[GGI_BUFFER_STATIC] = + gfx_pixmap_alloc_index_data(gfx_new_pixmap(mode.visible.x, mode.visible.y, GFX_RESID_NONE, 0, 0)); + + meta->priority_maps[GGI_BUFFER_BACK]->flags |= GFX_PIXMAP_FLAG_SCALED_INDEX; + meta->priority_maps[GGI_BUFFER_STATIC]->flags |= GFX_PIXMAP_FLAG_SCALED_INDEX; + + if (_open_meta_visuals(drv)) { + free(meta); + gfx_free_pixmap(drv, meta->priority_maps[GGI_BUFFER_BACK]); + gfx_free_pixmap(drv, meta->priority_maps[GGI_BUFFER_STATIC]); + ggiClose(meta->vis); + ggiExit(); + return GFX_FATAL; + } + + if (meta->frames < 2) { + meta->alt_back_buffer = (byte *) sci_malloc((pixelformat->size >> 3) * mode.visible.x * mode.visible.y); + meta->back_vis = ggiOpen("memory:pointer", meta->alt_back_buffer, NULL); + if (ggiSetSimpleMode(meta->back_vis, mode.visible.x, mode.visible.y, 1, GT_8BIT)) { + sciprintf("GFXGGI: Warning: Setting mode for memory visual failed\n"); + } + } else meta->alt_back_buffer = NULL; + + if (meta->frames < 3) { + meta->static_buffer = (byte *) sci_malloc((pixelformat->size >> 3) * mode.visible.x * mode.visible.y); + meta->static_vis = ggiOpen("memory:pointer", meta->static_buffer, NULL); + if (ggiSetSimpleMode(meta->static_vis, mode.visible.x, mode.visible.y, 1, GT_8BIT)) { + sciprintf("GFXGGI: Warning: Setting mode for memory visual #2 failed\n"); + } + } else meta->static_buffer = NULL; + + init_input_ggi(); + flags = 0; + + STATE->x_blank = x_blank; + STATE->y_blank = y_blank; + STATE->x_blank2 = x_blank >> 1; + STATE->y_blank2 = y_blank >> 1; + + return GFX_OK; +} + + +static void +ggi_exit(gfx_driver_t *drv) +{ + if (drv->state) { + gfx_free_pixmap(drv, STATE->priority_maps[0]); + gfx_free_pixmap(drv, STATE->priority_maps[1]); + + if (STATE->frames < 2) + ggiClose(STATE->back_vis); + + if (STATE->frames < 3) + ggiClose(STATE->static_vis); + + ggiClose(STATE->priority_visuals[0]); + ggiClose(STATE->priority_visuals[1]); + + ggiClose(VISUAL); + } + + gfx_free_mode(drv->mode); + + ggiExit(); + + if (FRAMES < 2) + free(STATE->alt_back_buffer); + + free(drv->state); +} + + +static inline ggi_visual_t +get_writeable_back_visual(gfx_driver_t *drv) +{ + if (STATE->frames > 1) { + ggiSetWriteFrame(VISUAL, 1); + return VISUAL; + } else + return STATE->back_vis; +} + +static inline ggi_visual_t +get_writeable_static_visual(gfx_driver_t *drv) +{ + if (STATE->frames > 2) { + ggiSetWriteFrame(VISUAL, 2); + return VISUAL; + } else + return STATE->static_vis; +} + +static inline ggi_pixel +ggi_map_color(gfx_driver_t *drv, ggi_visual_t vis, gfx_color_t color) +{ + if (MODE->palette) + return (ggi_pixel) color.visual.global_index; + else { + ggi_color gcolor; + gcolor.r = ((int) color.visual.r << 8) | color.visual.r; + gcolor.g = ((int) color.visual.g << 8) | color.visual.g; + gcolor.b = ((int) color.visual.b << 8) | color.visual.b; + gcolor.a = ((int) color.alpha << 8) | color.alpha; + + return ggiMapColor(vis, &gcolor); + } +} + +static int +ggi_draw_filled_rect(gfx_driver_t *drv, rect_t box, gfx_color_t color1, gfx_color_t color2, + gfx_rectangle_fill_t shade_mode); + +static int +ggi_draw_line(gfx_driver_t *drv, point_t start, point_t end, gfx_color_t color, + gfx_line_mode_t line_mode, gfx_line_style_t line_style) +{ + ggi_pixel pixel; + + int xw = MODE->xfact, yw = MODE->yfact; + int rx, ry, endx, endy; + int xc, yc; + + if (line_mode == GFX_LINE_MODE_FINE) + xw = yw = 1; + else { + xw = MODE->xfact, yw = MODE->yfact; + } + + rx = start.x; + ry = start.y; + endx = end.x; + endy = end.y; + + if ((rx == endx) && (ry == endy)) + return ggi_draw_filled_rect(drv, gfx_rect(rx, ry, xw, yw), color, color, GFX_SHADE_FLAT); + + if (color.mask & GFX_MASK_PRIORITY) { + ggi_visual_t privis = STATE->priority_visuals[GFX_BUFFER_BACK]; + + ggiSetGCForeground(privis, color.priority); + + for (xc = 0; xc < xw; xc++) + ggiDrawLine(privis, rx + xc, ry, endx + xc, endy); + if (yw > 0) + for (xc = 0; xc < xw; xc++) + ggiDrawLine(privis, rx + xc, ry + yw - 1, endx + xc, endy + yw - 1); + + if (yw > 1) { + for (yc = 1; yc < yw-1; yc++) + ggiDrawLine(privis, rx, ry + yc, endx, endy + yc); + + if (xw > 0) + for (yc = 1; yc < yw-1; yc++) + ggiDrawLine(privis, rx + xw - 1, ry + yc, endx + xw - 1, endy + yc); + } + } + + if (color.mask & GFX_MASK_VISUAL) { + ggi_visual_t vis; + + pixel = ggi_map_color(drv, VISUAL, color); + + vis = get_writeable_back_visual(drv); + + ggiSetGCForeground(vis, pixel); + + for (xc = 0; xc < xw; xc++) + ggiDrawLine(vis, rx + xc, ry, endx + xc, endy); + if (yw > 0) + for (xc = 0; xc < xw; xc++) + ggiDrawLine(vis, rx + xc, ry + yw - 1, endx + xc, endy + yw - 1); + + if (yw > 1) { + for (yc = 1; yc < yw-1; yc++) + ggiDrawLine(vis, rx, ry + yc, endx, endy + yc); + + if (xw > 0) + for (yc = 1; yc < yw-1; yc++) + ggiDrawLine(vis, rx + xw - 1, ry + yc, endx + xw - 1, endy + yc); + } + } + + return GFX_OK; +} + +static int +ggi_draw_filled_rect(gfx_driver_t *drv, rect_t box, gfx_color_t color1, gfx_color_t color2, + gfx_rectangle_fill_t shade_mode) +{ + if (color1.mask & GFX_MASK_VISUAL) { + ggi_pixel pixel = ggi_map_color(drv, VISUAL, color1); + ggi_visual_t vis = get_writeable_back_visual(drv); + + ggiSetGCForeground(vis, pixel); + ggiDrawBox(vis, box.x, box.y, box.xl, box.yl); + } + + if (color1.mask & GFX_MASK_PRIORITY) { + ggi_visual_t vis; + + ggiSetGCForeground(vis = STATE->priority_visuals[GFX_BUFFER_BACK], color1.priority); + ggiDrawBox(vis, box.x, box.y, box.xl, box.yl); + } + + return GFX_OK; +} + +/**************/ +/* Pixmap ops */ +/**************/ + +int +ggi_draw_pixmap(gfx_driver_t *drv, gfx_pixmap_t *pxm, int priority, + rect_t src, rect_t dest, gfx_buffer_t buffer) +{ + ggi_visual_t vis = VISUAL; + const ggi_directbuffer *dbuf; + byte *pri_map = NULL; + + if (dest.xl != src.xl || dest.yl != src.yl) { + GFXERROR("Attempt to scale pixmap (%dx%d)->(%dx%d): Not supported\n", + src.xl, src.yl, dest.xl, dest.yl); + return GFX_ERROR; + } + + switch (buffer) { + + case GFX_BUFFER_FRONT: + GFXERROR("Attempt to draw pixmap to front buffer\n"); + return GFX_ERROR; + + case GFX_BUFFER_BACK: + if (STATE->frames > 1) + ggiSetWriteFrame(VISUAL, 1); + else vis = STATE->back_vis; + pri_map = STATE->priority_maps[GGI_BUFFER_BACK]->index_data; + break; + + + case GFX_BUFFER_STATIC: + if (STATE->frames > 2) + ggiSetWriteFrame(VISUAL, 2); + else vis = STATE->static_vis; + pri_map = STATE->priority_maps[GGI_BUFFER_STATIC]->index_data; + break; + + + default: + GFXERROR("Unexpected buffer ID %d\n", buffer); + return GFX_ERROR; + + } + + assert(pri_map); + assert(vis); + + dbuf = ggiDBGetBuffer(vis, (vis == VISUAL)? buffer : 0); + if (!dbuf) { + GFXERROR("Could not acquire direct buffer!\n"); + return GFX_FATAL; + } + + if (dbuf->resource) { + if (ggiResourceAcquire(dbuf->resource, GGI_ACTYPE_WRITE)) { + GFXERROR("Failed to allocate resource for direct buffer!\n"); + return GFX_FATAL; + } + } + + if (dbuf->layout != blPixelLinearBuffer) { + char *type; + + switch (dbuf->layout) { + case blPixelPlanarBuffer: type = "planar"; + break; + + case blExtended: type = "extended"; + break; + + default: type = "invalid"; + + } + + GFXERROR("Error: Pixel buffer is %s! Non-linear buffers are not supported.\n", type); + + if (dbuf->resource) + ggiResourceRelease(dbuf->resource); + return GFX_FATAL; + } + + gfx_crossblit_pixmap(MODE, pxm, priority, src, dest, (byte *) dbuf->write, + dbuf->buffer.plb.stride, + pri_map, MODE->xfact * 320, 1, 0); + + /* ggiPutBox(vis, dest.x * MODE->xfact, dest.y * MODE->yfact, pxm->xl, pxm->yl, pxm->data); */ + + if (dbuf->resource) + ggiResourceRelease(dbuf->resource); + + return GFX_OK; +} + +int +ggi_grab_pixmap(gfx_driver_t *drv, rect_t src, gfx_pixmap_t *pxm, + gfx_map_mask_t map) +{ + ggi_visual_t vis = VISUAL; + int error; + + pxm->xl = src.xl; + pxm->yl = src.yl; + + if (STATE->frames > 1) + ggiSetReadFrame(VISUAL, 1); + else vis = STATE->back_vis; + + if (src.x < 0 || src.y < 0) { + GFXERROR("Attempt to grab pixmap from invalid coordinates (%d,%d)\n", src.x, src.y); + return GFX_ERROR; + } + + if (!pxm->data) { + GFXERROR("Attempt to grab pixmap to unallocated memory\n"); + return GFX_ERROR; + } + + if ((error = ggiGetBox(vis, src.x, src.y, src.xl, src.yl, pxm->data))) { + GFXERROR("ggiGetBox(%d, %d, %d, %d) returned error code %d\n", src.x, src.y, src.xl, src.yl, error); + return GFX_ERROR; + } + + return GFX_OK; +} + +/************/ +/* Misc ops */ +/************/ + + +static int +ggi_update(gfx_driver_t *drv, rect_t src, point_t dest, gfx_buffer_t buffer) +{ + int sx = src.x, sy = src.y; + int dx = dest.x, dy = dest.y; + int xl = src.xl, yl = src.yl; + + switch (buffer) { + case GFX_BUFFER_FRONT: + ggiSetWriteFrame(VISUAL, 0); + + if (STATE->frames < 2) + ggiCrossBlit(STATE->back_vis, sx, sy, xl, yl, VISUAL, dx + STATE->x_blank2, dy + STATE->y_blank2); + else { + ggiSetReadFrame(VISUAL, 1); + ggiCopyBox(VISUAL, sx, sy, xl, yl, dx + STATE->x_blank2, dy + STATE->y_blank2); + } + + break; + + case GFX_BUFFER_BACK: + if (src.x == dest.x && src.y == dest.y) + gfx_copy_pixmap_box_i(STATE->priority_maps[GGI_BUFFER_BACK], STATE->priority_maps[GGI_BUFFER_STATIC], src); + + if (STATE->frames > 1) + ggiSetWriteFrame(VISUAL, 1); + + if (STATE->frames > 2) { + ggiSetReadFrame(VISUAL, 2); + ggiCopyBox(VISUAL, sx, sy, xl, yl, dx, dy); + return GFX_OK; + } + + ggiCrossBlit(STATE->static_vis, sx, sy, xl, yl, STATE->back_vis, dx, dy); + + break; + + default: + GFXERROR("Invalid buffer %d in update!\n", buffer); + return GFX_ERROR; + } + return GFX_OK; +} + + +static int +ggi_set_palette(gfx_driver_t *drv, int index, byte red, byte green, byte blue) +{ + ggi_color color; + color.r = (red << 8) | red; + color.g = (green << 8) | green; + color.b = (blue << 8) | blue; + + /* DEBUG_POINTER(stderr,"Setting index %d to %04x %04x %04x\n", index, color.r, color.g, color.b); */ + + if (ggiSetPalette(VISUAL, index, 1, &color) < 0) + return GFX_ERROR; + else + return GFX_OK; +} + + +static int +ggi_set_static_buffer(gfx_driver_t *drv, gfx_pixmap_t *pic, gfx_pixmap_t *priority) +{ + ggi_visual_t vis = get_writeable_static_visual(drv); + + /* First, check if the priority map is sane */ + if (priority->index_xl != STATE->priority_maps[GGI_BUFFER_STATIC]->index_xl + || priority->index_yl != STATE->priority_maps[GGI_BUFFER_STATIC]->index_yl) { + GFXERROR("Invalid priority map: (%dx%d) vs expected (%dx%d)\n", + priority->index_xl, priority->index_yl, + STATE->priority_maps[GGI_BUFFER_STATIC]->index_xl, + STATE->priority_maps[GGI_BUFFER_STATIC]->index_yl); + return GFX_ERROR; + } + + ggiPutBox(vis, 0, 0, pic->xl, pic->yl, pic->data); + + memcpy(STATE->priority_maps[GGI_BUFFER_STATIC]->index_data, priority->index_data, + priority->index_xl * priority->index_yl); + + return GFX_OK; +} + + +/********************/ +/* Input management */ +/********************/ + + +struct timeval _sci_ggi_redraw_loopt, _sci_ggi_loopt; +/* timer variables */ + +int _sci_ggi_double_visual; + +static int buckybits; + +#define SCI_TIMEVAL_ADD(timev, addusec) \ + { timev.tv_usec += addusec; \ + while (timev.tv_usec >= 1000000L) { \ + timev.tv_usec -= 1000000L; \ + timev.tv_sec++; \ + }} + +#define SCI_TIMEVAL_LATER(later, earlier) \ + ((later.tv_sec == earlier.tv_sec)? (later.tv_usec >= earlier.tv_usec) \ + : (later.tv_sec >= earlier.tv_sec)) + + +static sci_event_t +ggi_get_event(gfx_driver_t *drv) +{ + struct timeval temptime = {0,0}; + gfx_ggi_struct_t *meta = (gfx_ggi_struct_t *) drv->state; + int modifiers; + + while (1) { + + if (ggiEventPoll(VISUAL, emAll, &temptime)) { + ggi_event event; + sci_event_t retval; + + ggiEventRead(VISUAL, &event, emAll); + + if (flags & SCI_GGI_SWAP_CTRL_CAPS + && ((event.any.type == evKeyPress) + || (event.any.type == evKeyRepeat) + || (event.any.type == evKeyRelease))) { + + switch (event.key.label) { + case GIIK_CtrlL: event.key.label = GIIK_CapsLock; break; + case GIIK_CapsLock: event.key.label = GIIK_CtrlL; break; + } + } + + switch (event.any.type) { + case evKeyPress: + case evKeyRepeat: + retval.type = SCI_EVT_KEYBOARD; + retval.data=-1; + retval.buckybits = 0; + switch(event.key.label) + { + case GIIK_P4: + case GIIK_Left: retval.data=SCI_K_LEFT; retval.buckybits = SCI_EVM_NUMLOCK; break; + case GIIK_P6: + case GIIK_Right: retval.data=SCI_K_RIGHT; retval.buckybits = SCI_EVM_NUMLOCK;break; + case GIIK_P8: + case GIIK_Up: retval.data=SCI_K_UP; retval.buckybits = SCI_EVM_NUMLOCK;break; + case GIIK_P2: + case GIIK_Down: retval.data=SCI_K_DOWN; retval.buckybits = SCI_EVM_NUMLOCK;break; + case GIIK_P7: + case GIIK_Home: retval.data=SCI_K_HOME; retval.buckybits = SCI_EVM_NUMLOCK;break; + case GIIK_P1: + case GIIK_End: retval.data=SCI_K_END; retval.buckybits = SCI_EVM_NUMLOCK;break; + case GIIK_P9: + case GIIK_PageUp: retval.data=SCI_K_PGUP; retval.buckybits = SCI_EVM_NUMLOCK;break; + case GIIK_P3: + case GIIK_PageDown: retval.data=SCI_K_PGDOWN; retval.buckybits = SCI_EVM_NUMLOCK;break; + case GIIK_P5: retval.data=SCI_K_CENTER; retval.buckybits = SCI_EVM_NUMLOCK;break; + + case GIIUC_Minus: + case GIIK_PMinus: retval.data = '-'; break; + case GIIUC_Plus: + case GIIK_PPlus: retval.data = '+'; break; + + case GIIUC_Grave: retval.data = '`'; break; +#if 0 + case GIIK_ShiftL: buckybits |= SCI_EVM_LSHIFT; break; + case GIIK_ShiftR: buckybits |= SCI_EVM_RSHIFT; break; + case GIIK_CtrlR: + case GIIK_CtrlL: buckybits |= SCI_EVM_CTRL; break; + case GIIK_AltL: + case GIIK_AltR: + case GIIK_MetaL: + case GIIK_MetaR: buckybits |= SCI_EVM_ALT; break; + case GIIK_CapsLock: buckybits ^= SCI_EVM_CAPSLOCK; break; + case GIIK_NumLock: buckybits ^= SCI_EVM_NUMLOCK; break; + case GIIK_ScrollLock: buckybits ^= SCI_EVM_SCRLOCK; break; +#endif + case GIIK_Insert: buckybits ^= SCI_EVM_INSERT; break; + case GIIK_PEnter: + case GIIK_Enter: retval.data='\r'; break; + case GIIUC_Tab: retval.data='\t'; break; + case GIIUC_Space: retval.data=' '; break; + case GIIUC_BackSpace: retval.data=SCI_K_BACKSPACE; break; + case GIIK_F1: retval.data = SCI_K_F1; break; + case GIIK_F2: retval.data = SCI_K_F2; break; + case GIIK_F3: retval.data = SCI_K_F3; break; + case GIIK_F4: retval.data = SCI_K_F4; break; + case GIIK_F5: retval.data = SCI_K_F5; break; + case GIIK_F6: retval.data = SCI_K_F6; break; + case GIIK_F7: retval.data = SCI_K_F7; break; + case GIIK_F8: retval.data = SCI_K_F8; break; + case GIIK_F9: retval.data = SCI_K_F9; break; + case GIIK_F10: retval.data = SCI_K_F10; break; + case GIIUC_Escape: retval.data = SCI_K_ESC; break; + + /*FIXME: Add all special keys in a sane way*/ + default: + { + if(event.key.label>='a' && event.key.label<='z') + retval.data=event.key.label-'a'+97; + if(event.key.label>='A' && event.key.label<='Z') + retval.data=event.key.label-'A'+97; + if(event.key.label>='0' && event.key.label<='9') + retval.data=event.key.label-'0'+48; + } + } + + modifiers = event.key.modifiers; + + buckybits = (buckybits & SCI_EVM_INSERT) | + (((modifiers & GII_MOD_CAPS)? SCI_EVM_LSHIFT | SCI_EVM_RSHIFT : 0) + | ((modifiers & GII_MOD_CTRL)? SCI_EVM_CTRL : 0) + | ((modifiers & (GII_MOD_ALT | GII_MOD_META))? SCI_EVM_ALT : 0) + | ((modifiers & GII_MOD_NUM)? SCI_EVM_NUMLOCK : 0) + | ((modifiers & GII_MOD_SCROLL)? SCI_EVM_SCRLOCK : 0)) + ^ ((modifiers & GII_MOD_SHIFT)? SCI_EVM_LSHIFT | SCI_EVM_RSHIFT : 0); + + if(retval.data==-1) continue; + retval.buckybits |= buckybits; + + return retval; + + case evKeyRelease: +#if 0 + switch(event.key.label) + { + case GIIK_ShiftL: buckybits &= ~SCI_EVM_LSHIFT; break; + case GIIK_ShiftR: buckybits &= ~SCI_EVM_RSHIFT; break; + case GIIK_CtrlR: + case GIIK_CtrlL: buckybits &= ~SCI_EVM_CTRL; break; + case GIIK_AltL: + case GIIK_AltR: + case GIIK_MetaL: + case GIIK_MetaR: buckybits &= ~SCI_EVM_ALT; break; + } +#endif + continue; + + case evPtrButtonPress: + retval.type = SCI_EVT_MOUSE_PRESS; + retval.data = event.pbutton.button; + retval.buckybits=buckybits; + + if (event.pbutton.button == GII_PBUTTON_LEFT) + retval.buckybits |= SCI_EVM_CTRL; + + if (event.pbutton.button == GII_PBUTTON_RIGHT) + retval.buckybits |= SCI_EVM_LSHIFT | SCI_EVM_RSHIFT; + return retval; + + case evPtrButtonRelease: + retval.type = SCI_EVT_MOUSE_RELEASE; + retval.data = event.pbutton.button; + retval.buckybits=buckybits; + + if (event.pbutton.button == GII_PBUTTON_LEFT) + retval.buckybits |= SCI_EVM_CTRL; + + if (event.pbutton.button == GII_PBUTTON_RIGHT) + retval.buckybits |= SCI_EVM_LSHIFT | SCI_EVM_RSHIFT; + + return retval; + + case evPtrAbsolute: + drv->pointer_x = event.pmove.x - STATE->x_blank2; + drv->pointer_y = event.pmove.y - STATE->y_blank2; + continue; + + case evPtrRelative: + drv->pointer_x += event.pmove.x; + drv->pointer_y += event.pmove.y; + /* FIXME: This may make the pointer too fast on high res! */ + continue; + } + } else { + sci_event_t retval; + + retval.type = SCI_EVT_NONE; /* Nothing happened */ + return retval; + } + + } +} + +static void +init_input_ggi() +{ + _sci_ggi_loopt = _sci_ggi_redraw_loopt; + buckybits = SCI_EVM_INSERT; /* Start up in "insert" mode */ + /* reset timers, leave them at current time to send redraw events ASAP */ + +} + + +int +ggi_usleep(gfx_driver_t* drv, long usec) +{ + struct timeval tv = {0, usec}; + + while(tv.tv_usec>0) + { + if(ggiEventPoll(VISUAL, emPtrMove, &tv)) + { + ggi_event e; + ggiEventRead(VISUAL, &e, emPtrMove); + switch(e.any.type) + { + case evPtrRelative: + { + drv->pointer_x+=e.pmove.x; + drv->pointer_y+=e.pmove.y; + } return GFX_OK; + case evPtrAbsolute: + { + drv->pointer_x=e.pmove.x - STATE->x_blank2; + drv->pointer_y=e.pmove.y - STATE->y_blank2; + } return GFX_OK; + } + } + } + return GFX_OK; +} + + + + +gfx_driver_t gfx_driver_ggi = { + "ggi", + GGI_DRIVER_VERSION, + SCI_GFX_DRIVER_MAGIC, + SCI_GFX_DRIVER_VERSION, + NULL, + 0,0, + GFX_CAPABILITY_FINE_LINES, GFX_DEBUG_POINTER + | GFX_DEBUG_UPDATES | GFX_DEBUG_PIXMAPS | GFX_DEBUG_BASIC, + ggi_set_param, + ggi_init_specific, + ggi_init, + ggi_exit, + ggi_draw_line, + ggi_draw_filled_rect, + NULL, + NULL, + ggi_draw_pixmap, + ggi_grab_pixmap, + ggi_update, + ggi_set_static_buffer, + NULL, + ggi_set_palette, + ggi_get_event, + ggi_usleep +}; + + +#endif /* HAVE_LIBGGI */ diff --git a/engines/sci/gfx/drivers/null_driver.c b/engines/sci/gfx/drivers/null_driver.c new file mode 100644 index 0000000000..e6821f56ef --- /dev/null +++ b/engines/sci/gfx/drivers/null_driver.c @@ -0,0 +1,211 @@ +/*************************************************************************** + null_driver.c Copyright (C) 2000 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) <jameson@linuxgames.com> + +***************************************************************************/ + +#include <gfx_driver.h> +#include <gfx_tools.h> + +static int debug_sleep = 0; +static int debug_draw = 0; + +static int +null_set_parameter(struct _gfx_driver *drv, char *attribute, char *value) +{ + printf("[GFX-NULL] Setting '%s' <- '%s'\n", attribute, value); + + return GFX_OK; +} + + + +static int +null_init_specific(struct _gfx_driver *drv, int xfact, int yfact, int bytespp) +{ + printf("[GFX-NULL] Initializing specific: %dx%d, %d bytespp\n", + xfact, yfact, bytespp); + + drv->mode = gfx_new_mode(xfact, yfact, bytespp, + 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0); + + return GFX_OK; +} + + +static int +null_init(struct _gfx_driver *drv) +{ + printf("[GFX-NULL] Initializing default\n"); + return null_init_specific(drv, 1, 1, 1); +} + +static void +null_exit(struct _gfx_driver *drv) +{ + printf("[GFX-NULL] Exitting\n"); +} + + + /*** Drawing operations ***/ + +static int +null_draw_line(struct _gfx_driver *drv, point_t start, point_t end, + gfx_color_t color, + gfx_line_mode_t line_mode, gfx_line_style_t line_style) +{ + if (debug_draw) + printf("[GFX-NULL] Line (%d,%d) -- (%d,%d)\n", + GFX_PRINT_POINT(start), + GFX_PRINT_POINT(end)); + return GFX_OK; +} + +static int +null_draw_filled_rect(struct _gfx_driver *drv, rect_t rect, + gfx_color_t color1, gfx_color_t color2, + gfx_rectangle_fill_t shade_mode) +{ + if (debug_draw) + printf("[GFX-NULL] Box (%d,%d)d(%d,%d)\n", + GFX_PRINT_RECT(rect)); + return GFX_OK; +} + + + /*** Pixmap operations ***/ + +static int +null_register_pixmap(struct _gfx_driver *drv, gfx_pixmap_t *pxm) +{ + return GFX_OK; +} + +static int +null_unregister_pixmap(struct _gfx_driver *drv, gfx_pixmap_t *pxm) +{ + return GFX_OK; +} + +static int +null_draw_pixmap(struct _gfx_driver *drv, gfx_pixmap_t *pxm, int priority, + rect_t src, rect_t dest, gfx_buffer_t buffer) +{ + return GFX_OK; +} + +static int +null_grab_pixmap(struct _gfx_driver *drv, rect_t src, gfx_pixmap_t *pxm, + gfx_map_mask_t map) +{ + return GFX_OK; + pxm->xl = src.xl; + pxm->yl = src.yl; +} + + + /*** Buffer operations ***/ + +static int +null_update(struct _gfx_driver *drv, rect_t src, point_t dest, gfx_buffer_t buffer) +{ + return GFX_OK; +} + +static int +null_set_static_buffer(struct _gfx_driver *drv, gfx_pixmap_t *pic, gfx_pixmap_t *priority) +{ + return GFX_OK; +} + + + /*** Mouse pointer operations ***/ + + +static int +null_set_pointer(struct _gfx_driver *drv, gfx_pixmap_t *pointer) +{ + return GFX_OK; +} + + + /*** Palette operations ***/ + +static int +null_set_palette(struct _gfx_driver *drv, int index, byte red, byte green, byte blue) +{ + return GFX_OK; +} + + + /*** Event management ***/ + +static sci_event_t +null_get_event(struct _gfx_driver *drv) +{ + sci_event_t input; + + input.type = SCI_EVT_NONE; + + return input; +} + + +static int +null_usec_sleep(struct _gfx_driver *drv, long usecs) +{ + if (debug_sleep) + sciprintf("[GFX-NULL] Sleeping %ld usecs...\n", usecs); + return GFX_OK; +} + +gfx_driver_t +gfx_driver_null = { + "null", + "0.1", + SCI_GFX_DRIVER_MAGIC, + SCI_GFX_DRIVER_VERSION, + NULL, + 0, 0, + GFX_CAPABILITY_WINDOWED, + GFX_DEBUG_POINTER | GFX_DEBUG_UPDATES | GFX_DEBUG_PIXMAPS | GFX_DEBUG_BASIC, + null_set_parameter, + null_init_specific, + null_init, + null_exit, + null_draw_line, + null_draw_filled_rect, + null_register_pixmap, + null_unregister_pixmap, + null_draw_pixmap, + null_grab_pixmap, + null_update, + null_set_static_buffer, + null_set_pointer, + null_set_palette, + null_get_event, + null_usec_sleep, + NULL +}; diff --git a/engines/sci/gfx/drivers/scummvm_driver.cpp b/engines/sci/gfx/drivers/scummvm_driver.cpp new file mode 100644 index 0000000000..dc38cb58e4 --- /dev/null +++ b/engines/sci/gfx/drivers/scummvm_driver.cpp @@ -0,0 +1,543 @@ +#include <common/scummsys.h> +#include <common/system.h> +#include <common/events.h> + +#include "gfx_driver.h" +#include "gfx_tools.h" + +struct _scummvm_driver_state { + gfx_pixmap_t *priority[2]; + byte *visual[3]; + byte *pointer_data[2]; + int xsize, ysize; + //int buckystate; + bool update_screen; + bool update_mouse; +}; + +#define S ((struct _scummvm_driver_state *)(drv->state)) + +static int +scummvm_init_specific(struct _gfx_driver *drv, int xfact, int yfact, int bytespp) +{ + int i; + + if (!drv->state) // = S + drv->state = new _scummvm_driver_state; + if (!drv->state) + return GFX_FATAL; + + S->xsize = xfact * 320; + S->ysize = yfact * 200; + + S->pointer_data[0] = NULL; + S->pointer_data[1] = NULL; + //S->buckystate = 0; + + for (i = 0; i < 2; i++) { + S->priority[i] = gfx_pixmap_alloc_index_data(gfx_new_pixmap(S->xsize, S->ysize, GFX_RESID_NONE, -i, -777)); + if (!S->priority[i]) { + printf("Out of memory: Could not allocate priority maps! (%dx%d)\n", + S->xsize, S->ysize); + return GFX_FATAL; + } + } + // create the visual buffers + for (i = 0; i < 3; i++) { + S->visual[i] = NULL; + S->visual[i] = new byte[S->xsize * S->ysize]; + if (!S->visual[i]) { + printf("Out of memory: Could not allocate visual buffers! (%dx%d)\n", + S->xsize, S->ysize); + return GFX_FATAL; + } + memset(S->visual[i], 0, S->xsize * S->ysize); + } + + drv->mode = gfx_new_mode(xfact, yfact, bytespp, + 0, 0, 0, 0, + 0, 0, 0, 0, 256, 0); + + return GFX_OK; +} + +static int +scummvm_init(struct _gfx_driver *drv) +{ + return scummvm_init_specific(drv, 1, 1, GFX_COLOR_MODE_INDEX); +} + +static void +scummvm_exit(struct _gfx_driver *drv) +{ + int i; + if (S) { + for (i = 0; i < 2; i++) { + gfx_free_pixmap(drv, S->priority[i]); + S->priority[i] = NULL; + } + + for (i = 0; i < 3; i++) { + delete[] S->visual[i]; + S->visual[i] = NULL; + } + + for (i = 0; i < 2; i++) + if (S->pointer_data[i]) { + delete[] S->pointer_data[i]; + S->pointer_data[i] = NULL; + } + + delete S; + } +} + + +// Drawing operations + +/* This code shamelessly lifted from the SDL_gfxPrimitives package */ +static void +lineColor2(byte *dst, int16 x1, int16 y1, int16 x2, int16 y2, uint32 color) +{ + int pixx, pixy; + int x,y; + int dx,dy; + int sx,sy; + int swaptmp; + uint8 *pixel; + + dx = x2 - x1; + dy = y2 - y1; + sx = (dx >= 0) ? 1 : -1; + sy = (dy >= 0) ? 1 : -1; + + dx = sx * dx + 1; + dy = sy * dy + 1; + pixx = 1; + pixy = 320; + pixel = ((uint8*)dst) + pixx * (int)x1 + pixy * (int)y1; + pixx *= sx; + pixy *= sy; + if (dx < dy) { + swaptmp = dx; dx = dy; dy = swaptmp; + swaptmp = pixx; pixx = pixy; pixy = swaptmp; + } + + /* Draw */ + x=0; + y=0; + for(; x < dx; x++, pixel += pixx) { + *pixel = color; + y += dy; + if (y >= dx) { + y -= dx; pixel += pixy; + } + } +} + +static int +scummvm_draw_line(struct _gfx_driver *drv, point_t start, point_t end, + gfx_color_t color, + gfx_line_mode_t line_mode, gfx_line_style_t line_style) +{ + uint32 scolor = color.visual.global_index; + int xsize = S->xsize; + int ysize = S->ysize; + + if (color.mask & GFX_MASK_VISUAL) { + point_t nstart, nend; + + nstart.x = start.x; + nstart.y = start.y; + nend.x = end.x; + nend.y = end.y; + + if (nstart.x < 0) + nstart.x = 0; + if (nend.x < 0) + nstart.x = 0; + if (nstart.y < 0) + nstart.y = 0; + if (nend.y < 0) + nend.y = 0; + if (nstart.x > xsize) + nstart.x = xsize; + if (nend.x >= xsize) + nend.x = xsize -1; + if (nstart.y > ysize) + nstart.y = ysize; + if (nend.y >= ysize) + nend.y = ysize -1; + + lineColor2(S->visual[1], (int16)nstart.x, (int16)nstart.y, + (int16)nend.x, (int16)nend.y, scolor); + + if (color.mask & GFX_MASK_PRIORITY) { + gfx_draw_line_pixmap_i(S->priority[0], nstart, nend, + color.priority); + } + } + + return GFX_OK; +} + +static int +scummvm_draw_filled_rect(struct _gfx_driver *drv, rect_t rect, + gfx_color_t color1, gfx_color_t color2, + gfx_rectangle_fill_t shade_mode) +{ + if (color1.mask & GFX_MASK_VISUAL) { + for (int i = rect.y; i < rect.y + rect.yl; i++) { + memset(S->visual[1] + i * S->xsize + rect.x, color1.visual.global_index, rect.xl); + } + } + + if (color1.mask & GFX_MASK_PRIORITY) + gfx_draw_box_pixmap_i(S->priority[0], rect, color1.priority); + + return GFX_OK; +} + + +// Pixmap operations + +static int +scummvm_draw_pixmap(struct _gfx_driver *drv, gfx_pixmap_t *pxm, int priority, + rect_t src, rect_t dest, gfx_buffer_t buffer) +{ + int bufnr = (buffer == GFX_BUFFER_STATIC)? 2:1; + int pribufnr = bufnr -1; + + if (dest.xl != src.xl || dest.yl != src.yl) { + printf("Attempt to scale pixmap (%dx%d)->(%dx%d): Not supported\n", + src.xl, src.yl, dest.xl, dest.yl); + return GFX_ERROR; + } + + gfx_crossblit_pixmap(drv->mode, pxm, priority, src, dest, + S->visual[bufnr], S->xsize, + S->priority[pribufnr]->index_data, + S->priority[pribufnr]->index_xl, 1, 0); + + return GFX_OK; +} + +static int +scummvm_grab_pixmap(struct _gfx_driver *drv, rect_t src, gfx_pixmap_t *pxm, + gfx_map_mask_t map) +{ + if (src.x < 0 || src.y < 0) { + printf("Attempt to grab pixmap from invalid coordinates (%d,%d)\n", src.x, src.y); + return GFX_ERROR; + } + + if (!pxm->data) { + printf("Attempt to grab pixmap to unallocated memory\n"); + return GFX_ERROR; + } + + switch (map) { + + case GFX_MASK_VISUAL: + pxm->xl = src.xl; + pxm->yl = src.yl; + for (int i = 0; i < src.yl; i++) { + memcpy(pxm->data + i * src.xl, S->visual[1] + (i + src.y) * S->xsize + src.x, src.xl); + } + break; + + case GFX_MASK_PRIORITY: + printf("FIXME: priority map grab not implemented yet!\n"); + break; + + default: + printf("Attempt to grab pixmap from invalid map 0x%02x\n", map); + return GFX_ERROR; + } + + return GFX_OK; +} + + +// Buffer operations + +static int +scummvm_update(struct _gfx_driver *drv, rect_t src, point_t dest, gfx_buffer_t buffer) +{ + //TODO + int data_source = (buffer == GFX_BUFFER_BACK)? 2 : 1; + int data_dest = data_source - 1; + + /* + if (src.x != dest.x || src.y != dest.y) { + printf("Updating %d (%d,%d)(%dx%d) to (%d,%d) on %d\n", buffer, src.x, src.y, + src.xl, src.yl, dest.x, dest.y, data_dest); + } else { + printf("Updating %d (%d,%d)(%dx%d) to %d\n", buffer, src.x, src.y, src.xl, src.yl, data_dest); + } + */ + + switch (buffer) { + case GFX_BUFFER_BACK: + //memcpy(S->visual[data_dest], S->visual[data_source], + // S->xsize * S->ysize); + for (int i = 0; i < src.yl; i++) { + memcpy(S->visual[data_dest] + (dest.y + i) * S->xsize + dest.x, + S->visual[data_source] + (src.y + i) * S->xsize + src.x, src.xl); + } + + if ((src.x == dest.x) && (src.y == dest.y)) + gfx_copy_pixmap_box_i(S->priority[0], S->priority[1], src); + break; + case GFX_BUFFER_FRONT: + memcpy(S->visual[data_dest], S->visual[data_source], + S->xsize * S->ysize); + + g_system->copyRectToScreen(S->visual[data_dest] + src.x + src.y * S->xsize, + S->xsize, dest.x, dest.y, src.xl, src.yl); + /* + g_system->copyRectToScreen(S->visual[data_dest], + S->xsize, 0, 0, S->xsize, S->ysize); + */ + S->update_screen = true; + break; + default: + GFXERROR("Invalid buffer %d in update!\n", buffer); + return GFX_ERROR; + } + + + return GFX_OK; +} + +static int +scummvm_set_static_buffer(struct _gfx_driver *drv, gfx_pixmap_t *pic, gfx_pixmap_t *priority) +{ + memcpy(S->visual[2], pic->data, S->xsize * S->ysize); + /*gfx_crossblit_pixmap(drv->mode, pic, 0, rect, rect, + S->visual[2], S->xsize, + S->priority[1]->index_data, + S->priority[1]->index_xl, 1, 0);*/ + + gfx_copy_pixmap_box_i(S->priority[1], priority, gfx_rect(0, 0, S->xsize, S->ysize)); + + return GFX_OK; +} + + +// Mouse pointer operations + +static int +scummvm_set_pointer(struct _gfx_driver *drv, gfx_pixmap_t *pointer) +{ + if (pointer == NULL) { + g_system->showMouse(false); + } else { + g_system->setMouseCursor(pointer->index_data, pointer->xl, pointer->yl, pointer->xoffset, pointer->yoffset); + g_system->showMouse(true); + } + + // Pointer pixmap or mouse position has changed + S->update_mouse = true; + return GFX_OK; +} + + +// Palette operations + +static int +scummvm_set_palette(struct _gfx_driver *drv, int index, byte red, byte green, byte blue) +{ + byte color[] = {red, green, blue, 255}; + g_system->setPalette(color, index, 1); + return GFX_OK; +} + + +// Event management + +static sci_event_t +scummvm_get_event(struct _gfx_driver *drv) +{ + sci_event_t input; + input.type = SCI_EVT_NONE; + + Common::EventManager *em = g_system->getEventManager(); + Common::Event ev; + + bool found = em->pollEvent(ev); + Common::Point p = ev.mouse; + + // Don't generate events for mouse movement + while (found && ev.type == Common::EVENT_MOUSEMOVE) { + found = em->pollEvent(ev); + p = ev.mouse; + drv->pointer_x = p.x; + drv->pointer_y = p.y; + S->update_mouse = true; + } + + // Update the screen here, since it's called very often + if (S->update_mouse) + g_system->warpMouse(drv->pointer_x, drv->pointer_y); + if (S->update_screen || S->update_mouse) { + g_system->updateScreen(); + S->update_screen = false; + S->update_mouse = false; + } + + if (found && !ev.synthetic && ev.type != Common::EVENT_MOUSEMOVE) { + int modifiers; + if (ev.type == Common::EVENT_KEYDOWN) + modifiers = ev.kbd.flags; + else + modifiers = em->getModifierState(); + + input.buckybits = + ((modifiers & Common::KBD_ALT) ? SCI_EVM_ALT : 0) | + ((modifiers & Common::KBD_CTRL) ? SCI_EVM_CTRL : 0) | + ((modifiers & Common::KBD_SHIFT) ? SCI_EVM_LSHIFT | SCI_EVM_RSHIFT : 0); + //TODO: SCI_EVM_SCRLOCK SCI_EVM_NUMLOCK SCI_EVM_CAPSLOCK SCI_EVM_INSERT + + switch (ev.type) { + // Keyboard events + case Common::EVENT_KEYDOWN: + input.data = ev.kbd.keycode; + input.character = ev.kbd.ascii; + + if (!(input.data & 0xFF00)) { + // Directly accept most common keys without conversion + input.type = SCI_EVT_KEYBOARD; + if (input.data == Common::KEYCODE_TAB) { + // Tab + input.type = SCI_EVT_KEYBOARD; + input.data = SCI_K_TAB; + if (input.buckybits & (SCI_EVM_LSHIFT | SCI_EVM_RSHIFT)) + input.character = SCI_K_SHIFT_TAB; + else + input.character = SCI_K_TAB; + } + } else if ((input.data >= Common::KEYCODE_F1) && input.data <= Common::KEYCODE_F10) { + // F1-F10 + input.type = SCI_EVT_KEYBOARD; + // SCI_K_F1 == 59 << 8 + // SCI_K_SHIFT_F1 == 84 << 8 + input.data = (input.data - Common::KEYCODE_F1 + SCI_K_F1) << 8; + if (input.buckybits & (SCI_EVM_LSHIFT | SCI_EVM_RSHIFT)) + input.character = input.data + ((SCI_K_SHIFT_F1 - SCI_K_F1) << 8); + else + input.character = input.data; + } else { + // Special keys that need conversion + input.type = SCI_EVT_KEYBOARD; + switch (ev.kbd.keycode) { + case Common::KEYCODE_UP: + input.data = SCI_K_UP; + break; + case Common::KEYCODE_DOWN: + input.data = SCI_K_DOWN; + break; + case Common::KEYCODE_RIGHT: + input.data = SCI_K_RIGHT; + break; + case Common::KEYCODE_LEFT: + input.data = SCI_K_LEFT; + break; + case Common::KEYCODE_INSERT: + input.data = SCI_K_INSERT; + break; + case Common::KEYCODE_HOME: + input.data = SCI_K_HOME; + break; + case Common::KEYCODE_END: + input.data = SCI_K_END; + break; + case Common::KEYCODE_PAGEUP: + input.data = SCI_K_PGUP; + break; + case Common::KEYCODE_PAGEDOWN: + input.data = SCI_K_PGDOWN; + break; + case Common::KEYCODE_DELETE: + input.data = SCI_K_DELETE; + break; + //TODO: SCI_K_CENTER + default: + input.type = SCI_EVT_NONE; + break; + } + input.character = input.data; + } + break; + + // Mouse events + case Common::EVENT_LBUTTONDOWN: + input.type = SCI_EVT_MOUSE_PRESS; + input.data = 1; + drv->pointer_x = p.x; + drv->pointer_y = p.y; + break; + case Common::EVENT_RBUTTONDOWN: + input.type = SCI_EVT_MOUSE_PRESS; + input.data = 2; + drv->pointer_x = p.x; + drv->pointer_y = p.y; + break; + case Common::EVENT_LBUTTONUP: + input.type = SCI_EVT_MOUSE_RELEASE; + input.data = 1; + drv->pointer_x = p.x; + drv->pointer_y = p.y; + break; + case Common::EVENT_RBUTTONUP: + input.type = SCI_EVT_MOUSE_RELEASE; + input.data = 2; + drv->pointer_x = p.x; + drv->pointer_y = p.y; + break; + + // Misc events + case Common::EVENT_QUIT: + input.type = SCI_EVT_QUIT; + break; + } + } + + return input; +} + +static int +scummvm_usec_sleep(struct _gfx_driver *drv, long usecs) +{ + g_system->delayMillis(usecs/1000); + return GFX_OK; +} + +gfx_driver_t +gfx_driver_scummvm = { + "ScummVM", + "0.1", + SCI_GFX_DRIVER_MAGIC, + SCI_GFX_DRIVER_VERSION, + NULL, + 0, 0, + GFX_CAPABILITY_MOUSE_POINTER | GFX_CAPABILITY_COLOR_MOUSE_POINTER | GFX_CAPABILITY_MOUSE_SUPPORT | GFX_CAPABILITY_FINE_LINES | GFX_CAPABILITY_WINDOWED, + 0, + NULL, + scummvm_init_specific, + scummvm_init, + scummvm_exit, + scummvm_draw_line, + scummvm_draw_filled_rect, + NULL, + NULL, + scummvm_draw_pixmap, + scummvm_grab_pixmap, + scummvm_update, + scummvm_set_static_buffer, + scummvm_set_pointer, + scummvm_set_palette, + scummvm_get_event, + scummvm_usec_sleep, + NULL +}; diff --git a/engines/sci/gfx/drivers/sdl_driver.c b/engines/sci/gfx/drivers/sdl_driver.c new file mode 100644 index 0000000000..e52cea9db5 --- /dev/null +++ b/engines/sci/gfx/drivers/sdl_driver.c @@ -0,0 +1,1235 @@ +/*************************************************************************** + sdl_driver.c Copyright (C) 2001 Solomon Peachy + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + Solomon Peachy <pizza@shaftnet.org> + +***************************************************************************/ + +/* set optimisations for Win32: */ +/* g on: enable global optimizations */ +/* t on: use fast code */ +/* y on: suppress creation of frame pointers on stack */ +/* s off: disable minimize size code */ +#ifdef _WIN32 +# include <memory.h> +# ifndef SATISFY_PURIFY +# pragma optimize( "s", off ) +# pragma optimize( "gty", on ) +# pragma intrinsic( memcpy, memset ) +# endif +#endif + +#include <sci_memory.h> + +#include <gfx_driver.h> +#ifdef HAVE_SDL +#include <gfx_tools.h> + +#if !defined(_MSC_VER) +# include <sys/time.h> +#endif + +#include <SDL_config.h> +#undef HAVE_ICONV +#undef HAVE_ICONV_H +#undef HAVE_ALLOCA_H + +#include <SDL.h> + +#ifndef SDL_DISABLE +# define SDL_DISABLE 0 +#endif +#ifndef SDL_ALPHA_OPAQUE +# define SDL_ALPHA_OPAQUE 255 +#endif + +#define SCI_SDL_HANDLE_NORMAL 0 +#define SCI_SDL_HANDLE_GRABBED 1 + +#define SCI_SDL_SWAP_CTRL_CAPS (1 << 0) +#define SCI_SDL_FULLSCREEN (1 << 2) + +static int +sdl_usec_sleep(struct _gfx_driver *drv, long usecs); + +static int flags = 0; + +struct _sdl_state { + int used_bytespp; + gfx_pixmap_t *priority[2]; + SDL_Color colors[256]; + SDL_Surface *visual[3]; + SDL_Surface *primary; + int buckystate; + byte *pointer_data[2]; + int alpha_mask; + int SDL_alpha_shift; + int SDL_alpha_loss; +}; + +#define S ((struct _sdl_state *)(drv->state)) + +#define XFACT drv->mode->xfact +#define YFACT drv->mode->yfact + +#define DEBUGB if (drv->debug_flags & GFX_DEBUG_BASIC && ((debugline = __LINE__))) sdlprintf +#define DEBUGU if (drv->debug_flags & GFX_DEBUG_UPDATES && ((debugline = __LINE__))) sdlprintf +#define DEBUGPXM if (drv->debug_flags & GFX_DEBUG_PIXMAPS && ((debugline = __LINE__))) sdlprintf +#define DEBUGPTR if (drv->debug_flags & GFX_DEBUG_POINTER && ((debugline = __LINE__))) sdlprintf +#define SDLERROR if ((debugline = __LINE__)) sdlprintf +#define SDLPRINTF if ((debugline = __LINE__)) sdlprintf + +#define ALPHASURFACE (S->used_bytespp == 4) + +static int debugline = 0; + +static void +sdlprintf(const char *fmt, ...) +{ + va_list argp; + fprintf(stderr,"GFX-SDL %d:", debugline); + va_start(argp, fmt); + vfprintf(stderr, fmt, argp); + va_end(argp); +} + +static int +sdl_init_libsdl(struct _gfx_driver *drv) +{ + if (SDL_Init(SDL_INIT_VIDEO|SDL_INIT_NOPARACHUTE)) { + DEBUGB("Failed to init SDL\n"); + return -1; + } + + SDL_EnableUNICODE(SDL_ENABLE); + + return 0; +} + +static int +sdl_alloc_primary(struct _gfx_driver *drv, int xfact, int yfact, int bytespp) +{ + int i = SDL_HWSURFACE | SDL_HWPALETTE; + + if (flags & SCI_SDL_FULLSCREEN) { + i |= SDL_FULLSCREEN; + } + + S->primary = SDL_SetVideoMode(xfact * 320, yfact * 200, bytespp << 3, i); + + if (!S->primary) { + SDLERROR("Could not set up a primary SDL surface!\n"); + return -1; + } + + if (S->primary->format->BytesPerPixel != bytespp) { + SDLERROR("Could not set up a primary SDL surface of depth %d bpp!\n",bytespp); + S->primary = NULL; + return -1; + } + + /* Set windowed flag */ + if (S->primary->flags & SDL_FULLSCREEN) + drv->capabilities &= ~GFX_CAPABILITY_WINDOWED; + else + drv->capabilities |= GFX_CAPABILITY_WINDOWED; + + return 0; +} + +static int +sdl_blit_surface(gfx_driver_t *drv, + SDL_Surface *src, SDL_Rect *srcrect, SDL_Surface *dst, SDL_Rect *dstrect) +{ + if (S->used_bytespp == 1) { + SDL_SetColors(src, S->colors, 0, 256); + SDL_SetColors(dst, S->colors, 0, 256); + } + return SDL_BlitSurface(src, srcrect, dst, dstrect); +} + +static int +sdl_set_parameter(struct _gfx_driver *drv, char *attribute, char *value) +{ + if (!strncmp(attribute, "swap_ctrl_caps", 14) || + !strncmp(attribute, "swap_caps_ctrl", 14)) { + if (string_truep(value)) + flags |= SCI_SDL_SWAP_CTRL_CAPS; + else + flags &= ~SCI_SDL_SWAP_CTRL_CAPS; + return GFX_OK; + } + + if (!strncmp(attribute, "fullscreen", 10)) { + if (string_truep(value)) + flags |= SCI_SDL_FULLSCREEN; + else + flags &= ~SCI_SDL_FULLSCREEN; + + return GFX_OK; + } + + + SDLERROR("Attempt to set sdl parameter \"%s\" to \"%s\"\n", attribute, value); + return GFX_ERROR; +} + +static int +sdl_init_specific(struct _gfx_driver *drv, int xfact, int yfact, int bytespp) +{ + int red_shift, green_shift, blue_shift, alpha_shift; + int xsize = xfact * 320; + int ysize = yfact * 200; + + int i; + +#ifdef _MSC_VER /* Win32 doesn't support mouse pointers greater than 64x64 */ + if (xfact > 2 || yfact > 2) + drv->capabilities &= ~GFX_CAPABILITY_MOUSE_POINTER; +#endif +#if defined(__BEOS__) || defined(__amigaos4__) /* BeOS has been reported not to work well with the mouse pointer at all */ + drv->capabilities &= ~GFX_CAPABILITY_MOUSE_POINTER; +#endif + + if (sdl_init_libsdl(drv)) + return GFX_FATAL; + + if (!drv->state /* = S */) + drv->state = sci_malloc(sizeof(struct _sdl_state)); + if (!drv->state) + return GFX_FATAL; + + if (xfact < 1 || yfact < 1 || bytespp < 1 || bytespp > 4) { + SDLERROR("Internal error: Attempt to open window w/ scale factors (%d,%d) and bpp=%d!\n", + xfact, yfact, bytespp); + } + + if (sdl_alloc_primary(drv, xfact, yfact, bytespp)) + return GFX_FATAL; + + S->used_bytespp = bytespp; + + printf("Using primary SDL surface of %d,%d @%d bpp\n", + xsize, ysize, bytespp << 3); + + /* if (S->primary->format->BytesPerPixel == 4) { + S->alpha_mask = 0xff000000; + S->SDL_alpha_shift = 24; + S->SDL_alpha_loss = 0; + alpha_shift = 0; + } else { */ + S->alpha_mask = S->primary->format->Amask; + S->SDL_alpha_shift = S->primary->format->Ashift; + S->SDL_alpha_loss = S->primary->format->Aloss; + alpha_shift = bytespp << 3; + /* }*/ + + /* clear palette */ + for (i = 0; i < 256; i++) { + S->colors[i].r = (i & 1)? 0 : 0; + S->colors[i].g = (i & 2)? 0 : 0; + S->colors[i].b = (i & 4)? 0 : 0; + } + if (bytespp == 1) + SDL_SetColors(S->primary, S->colors, 0, 256); + + /* create an input event mask */ + SDL_EventState(SDL_ACTIVEEVENT, SDL_IGNORE); + SDL_EventState(SDL_VIDEORESIZE, SDL_IGNORE); + SDL_EventState(SDL_KEYUP, SDL_IGNORE); + + SDL_WM_SetCaption("FreeSCI", "freesci"); + + SDL_ShowCursor(SDL_DISABLE); + S->pointer_data[0] = NULL; + S->pointer_data[1] = NULL; + + S->buckystate = 0; + + if (bytespp == 1) { + red_shift = green_shift = blue_shift = alpha_shift = 0; + } else { + red_shift = 24 - S->primary->format->Rshift + S->primary->format->Rloss; + green_shift = 24 - S->primary->format->Gshift + S->primary->format->Gloss; + blue_shift = 24 - S->primary->format->Bshift + S->primary->format->Bloss; + } + + printf("%08x %08x %08x %08x %d/%d=%d %d/%d=%d %d/%d=%d %d/%d=%d\n", + S->primary->format->Rmask, + S->primary->format->Gmask, + S->primary->format->Bmask, + S->alpha_mask, + /* S->primary->format->Amask,*/ + S->primary->format->Rshift, + S->primary->format->Rloss, + red_shift, + S->primary->format->Gshift, + S->primary->format->Gloss, + green_shift, + S->primary->format->Bshift, + S->primary->format->Bloss, + blue_shift, + S->SDL_alpha_shift, + S->SDL_alpha_loss, + /* + S->primary->format->Ashift, + S->primary->format->Aloss, */ + alpha_shift); + + for (i = 0; i < 2; i++) { + S->priority[i] = gfx_pixmap_alloc_index_data(gfx_new_pixmap(xsize, ysize, GFX_RESID_NONE, -i, -777)); + if (!S->priority[i]) { + SDLERROR("Out of memory: Could not allocate priority maps! (%dx%d)\n", + xsize, ysize); + return GFX_FATAL; + } + } + + /* create the visual buffers */ + for (i = 0; i < 3; i++) { + S->visual[i] = SDL_CreateRGBSurface(SDL_SRCALPHA, + /* SDL_HWSURFACE | SDL_SWSURFACE, */ + xsize, ysize, + bytespp << 3, + S->primary->format->Rmask, + S->primary->format->Gmask, + S->primary->format->Bmask, + S->alpha_mask); + if (S->visual[i] == NULL) { + SDLERROR("Could not set up visual buffers!\n"); + return GFX_FATAL; + } + + if (ALPHASURFACE) + SDL_SetAlpha(S->visual[i],SDL_SRCALPHA,SDL_ALPHA_OPAQUE); + + if (SDL_FillRect(S->primary, NULL, SDL_MapRGB(S->primary->format, 0,0,0))) + SDLERROR("Couldn't fill backbuffer!\n"); + } + + SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL); + + drv->mode = gfx_new_mode(xfact, yfact, bytespp, + S->primary->format->Rmask, + S->primary->format->Gmask, + S->primary->format->Bmask, + S->alpha_mask, + red_shift, green_shift, blue_shift, alpha_shift, + (bytespp == 1)? 256 : 0, 0); /*GFX_MODE_FLAG_REVERSE_ALPHA);*/ + + return GFX_OK; +} + +static int +sdl_init(struct _gfx_driver *drv) +{ + int depth = 0; + int i; + + if (sdl_init_libsdl(drv)) + return GFX_FATAL; + + i = SDL_HWSURFACE | SDL_HWPALETTE; + if (flags & SCI_SDL_FULLSCREEN) { + i |= SDL_FULLSCREEN; + } + + depth = SDL_VideoModeOK(640,400, 32, i); + if (depth && (! sdl_init_specific(drv, 2, 2, depth >> 3 ))) + return GFX_OK; + + DEBUGB("Failed to find visual!\n"); + return GFX_FATAL; +} + +static void +sdl_exit(struct _gfx_driver *drv) +{ + int i; + if (S) { + for (i = 0; i < 2; i++) { + gfx_free_pixmap(drv, S->priority[i]); + S->priority[i] = NULL; + } + + for (i = 0; i < 3; i++) { + SDL_FreeSurface(S->visual[i]); + S->visual[i] = NULL; + } + + SDL_FreeCursor(SDL_GetCursor()); + + for (i = 0; i < 2; i++) + if (S->pointer_data[i]) { + free(S->pointer_data[i]); + S->pointer_data[i] = NULL; + } + + } + + SDL_QuitSubSystem(SDL_INIT_VIDEO); + + if (!SDL_WasInit(SDL_INIT_EVERYTHING)) { + SDLPRINTF("No active SDL subsystems found.. shutting down SDL\n"); + SDL_Quit(); + } +} + +static void +toggle_fullscreen(struct _gfx_driver *drv) +{ + rect_t src; + point_t dest; + + flags ^= SCI_SDL_FULLSCREEN; + if (sdl_alloc_primary(drv, XFACT, YFACT, drv->mode->bytespp)) { + SDLERROR("failed to switch to full-screen mode\n"); + /* Failed to set mode, revert to previous */ + flags ^= SCI_SDL_FULLSCREEN; + + if (sdl_alloc_primary(drv, XFACT, YFACT, drv->mode->bytespp)) { + /* This shouldn't happen... */ + SDLERROR("failed to revert to previous display mode\n"); + exit(-1); + } + } + + src.x = 0; + src.y = 0; + src.xl = XFACT * 320; + src.yl = YFACT * 200; + dest.x = 0; + dest.y = 0; + + drv->update(drv, src, dest, GFX_BUFFER_FRONT); +} + +/*** Drawing operations ***/ + +static Uint32 +sdl_map_color(gfx_driver_t *drv, gfx_color_t color) +{ + int opacity = 255 - color.alpha; + + if (drv->mode->palette && opacity < 255) { + if (opacity < 127) + opacity = 0; + else + opacity = 255; + + } + + if (drv->mode->palette) + return color.visual.global_index; + + return SDL_MapRGBA(S->visual[0]->format, + color.visual.r, + color.visual.g, + color.visual.b, + opacity); +} + +/* This code shamelessly lifted from the SDL_gfxPrimitives package */ +static void lineColor2(SDL_Surface *dst, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2, Uint32 color) +{ + int pixx, pixy; + int x,y; + int dx,dy; + int sx,sy; + int swaptmp; + Uint8 *pixel; + + dx = x2 - x1; + dy = y2 - y1; + sx = (dx >= 0) ? 1 : -1; + sy = (dy >= 0) ? 1 : -1; + + dx = sx * dx + 1; + dy = sy * dy + 1; + pixx = dst->format->BytesPerPixel; + pixy = dst->pitch; + pixel = ((Uint8*)dst->pixels) + pixx * (int)x1 + pixy * (int)y1; + pixx *= sx; + pixy *= sy; + if (dx < dy) { + swaptmp = dx; dx = dy; dy = swaptmp; + swaptmp = pixx; pixx = pixy; pixy = swaptmp; + } + +/* Draw */ + x=0; + y=0; + switch(dst->format->BytesPerPixel) { + case 1: + for(; x < dx; x++, pixel += pixx) { + *pixel = color; + y += dy; + if (y >= dx) { + y -= dx; pixel += pixy; + } + } + break; + case 2: + for (; x < dx; x++, pixel += pixx) { + *(Uint16*)pixel = color; + y += dy; + if (y >= dx) { + y -= dx; + pixel += pixy; + } + } + break; + case 3: + for(; x < dx; x++, pixel += pixx) { + if(SDL_BYTEORDER == SDL_BIG_ENDIAN) { + pixel[0] = (color >> 16) & 0xff; + pixel[1] = (color >> 8) & 0xff; + pixel[2] = color & 0xff; + } else { + pixel[0] = color & 0xff; + pixel[1] = (color >> 8) & 0xff; + pixel[2] = (color >> 16) & 0xff; + } + y += dy; + if (y >= dx) { + y -= dx; + pixel += pixy; + } + } + break; + case 4: + for(; x < dx; x++, pixel += pixx) { + *(Uint32*)pixel = color; + y += dy; + if (y >= dx) { + y -= dx; + pixel += pixy; + } + } + break; + default: + fprintf(stderr, "invalid depth\n"); + } + +} + +static int +sdl_draw_line(struct _gfx_driver *drv, point_t start, point_t end, gfx_color_t color, + gfx_line_mode_t line_mode, gfx_line_style_t line_style) +{ + Uint32 scolor; + int xfact = (line_mode == GFX_LINE_MODE_FINE)? 1: XFACT; + int yfact = (line_mode == GFX_LINE_MODE_FINE)? 1: YFACT; + int xsize = S->visual[1]->w; + int ysize = S->visual[1]->h; + + if (color.mask & GFX_MASK_VISUAL) { + int xc, yc; + point_t nstart, nend; + + scolor = sdl_map_color(drv, color); + + for (xc = 0; xc < xfact; xc++) + for (yc = 0; yc < yfact; yc++) { + nstart.x = start.x + xc; + nstart.y = start.y + yc; + nend.x = end.x + xc; + nend.y = end.y + yc; + + if (nstart.x < 0) + nstart.x = 0; + if (nend.x < 0) + nstart.x = 0; + if (nstart.y < 0) + nstart.y = 0; + if (nend.y < 0) + nend.y = 0; + if (nstart.x > xsize) + nstart.x = xsize; + if (nend.x >= xsize) + nend.x = xsize -1; + if (nstart.y > ysize) + nstart.y = ysize; + if (nend.y >= ysize) + nend.y = ysize -1; + +#if 0 + fprintf(stderr, "draw %d %d to %d %d %08x %d %d\n", nstart.x, + nstart.y, nend.x, nend.yl, scolor, xsize, ysize); +#endif + + SDL_LockSurface(S->visual[1]); + lineColor2(S->visual[1], (Sint16)nstart.x, (Sint16)nstart.y, + (Sint16)nend.x, (Sint16)nend.y, scolor); + SDL_UnlockSurface(S->visual[1]); + + if (color.mask & GFX_MASK_PRIORITY) { + gfx_draw_line_pixmap_i(S->priority[0], nstart, nend, + color.priority); + } + } + } + + return GFX_OK; +} + +static int +sdl_draw_filled_rect(struct _gfx_driver *drv, rect_t rect, + gfx_color_t color1, gfx_color_t color2, + gfx_rectangle_fill_t shade_mode) +{ + Uint32 color; + SDL_Rect srect; + + if (color1.mask & GFX_MASK_VISUAL) { + color = sdl_map_color(drv, color1); + + srect.x = rect.x; + srect.y = rect.y; + srect.w = rect.xl; + srect.h = rect.yl; + + if (SDL_FillRect(S->visual[1], &srect, color)) + SDLERROR("Can't fill rect"); + } + + if (color1.mask & GFX_MASK_PRIORITY) + gfx_draw_box_pixmap_i(S->priority[0], rect, color1.priority); + + return GFX_OK; +} + +/*** Pixmap operations ***/ + +static int +sdl_register_pixmap(struct _gfx_driver *drv, gfx_pixmap_t *pxm) +{ + SDL_Surface *reg_surface; + + if (pxm->internal.info) { + SDLERROR("Attempt to register pixmap twice!\n"); + return GFX_ERROR; + } + + reg_surface = + SDL_CreateRGBSurfaceFrom(pxm->data, pxm->xl, pxm->yl, + S->used_bytespp << 3, + S->used_bytespp * pxm->xl, + S->primary->format->Rmask, + S->primary->format->Gmask, + S->primary->format->Bmask, + S->alpha_mask); + + if (ALPHASURFACE) + SDL_SetAlpha(reg_surface, SDL_SRCALPHA,SDL_ALPHA_OPAQUE); + + pxm->internal.handle = SCI_SDL_HANDLE_NORMAL; + + DEBUGPXM("Registered surface %d/%d/%d at %p (%dx%d)\n", pxm->ID, pxm->loop, pxm->cel, + pxm->internal.info, pxm->xl, pxm->yl); + + pxm->internal.info = reg_surface; + + return GFX_OK; +} + +static int +sdl_unregister_pixmap(struct _gfx_driver *drv, gfx_pixmap_t *pxm) +{ + DEBUGPXM("Freeing surface %d/%d/%d at %p\n", pxm->ID, pxm->loop, pxm->cel, + pxm->internal.info); + + if (!pxm->internal.info) { + SDLERROR("Attempt to unregister pixmap twice!\n"); + return GFX_ERROR; + } + + SDL_FreeSurface((SDL_Surface *) pxm->internal.info); + pxm->internal.info = NULL; + if (pxm->internal.handle != SCI_SDL_HANDLE_GRABBED) + free(pxm->data); + pxm->data = NULL; + return GFX_OK; +} + +static int +sdl_draw_pixmap(struct _gfx_driver *drv, gfx_pixmap_t *pxm, int priority, + rect_t src, rect_t dest, gfx_buffer_t buffer) +{ + int bufnr = (buffer == GFX_BUFFER_STATIC)? 2:1; + int pribufnr = bufnr -1; + + SDL_Surface *temp; + SDL_Rect srect; + SDL_Rect drect; + + if (dest.xl != src.xl || dest.yl != src.yl) { + SDLERROR("Attempt to scale pixmap (%dx%d)->(%dx%d): Not supported\n", + src.xl, src.yl, dest.xl, dest.yl); + return GFX_ERROR; + } + + srect.x = src.x; + srect.y = src.y; + srect.w = src.xl; + srect.h = src.yl; + drect.x = dest.x; + drect.y = dest.y; + drect.w = dest.xl; + drect.h = dest.yl; + + DEBUGU("Drawing %d (%d,%d)(%dx%d) onto (%d,%d)\n", pxm, srect.x, srect.y, + srect.w, srect.h, drect.x, drect.y); + + if (pxm->internal.handle == SCI_SDL_HANDLE_GRABBED) { + if (sdl_blit_surface(drv, (SDL_Surface *)pxm->internal.info, &srect , + S->visual[bufnr], &drect )) { + SDLERROR("blt failed"); + return GFX_ERROR; + } + return GFX_OK; + } + + temp = SDL_CreateRGBSurface(SDL_SWSURFACE, drect.w, drect.h, + S->used_bytespp << 3, + S->primary->format->Rmask, + S->primary->format->Gmask, + S->primary->format->Bmask, + S->alpha_mask); + + if (ALPHASURFACE) + SDL_SetAlpha(temp, SDL_SRCALPHA,SDL_ALPHA_OPAQUE); + + if (!temp) { + SDLERROR("Failed to allocate SDL surface"); + return GFX_ERROR; + } + + srect.x = dest.x; + srect.y = dest.y; + drect.x = 0; + drect.y = 0; + + if(sdl_blit_surface(drv, S->visual[bufnr], &srect, temp, &drect)) + SDLERROR("blt failed"); + + SDL_LockSurface(temp); + gfx_crossblit_pixmap(drv->mode, pxm, priority, src, dest, + (byte *) temp->pixels, temp->pitch, + S->priority[pribufnr]->index_data, + S->priority[pribufnr]->index_xl, 1, + GFX_CROSSBLIT_FLAG_DATA_IS_HOMED); + SDL_UnlockSurface(temp); + + srect.x = 0; + srect.y = 0; + drect.x = dest.x; + drect.y = dest.y; + + if(sdl_blit_surface(drv, temp, &srect, S->visual[bufnr], &drect)) + SDLERROR("blt failed"); + + SDL_FreeSurface(temp); + return GFX_OK; +} + +static int +sdl_grab_pixmap(struct _gfx_driver *drv, rect_t src, gfx_pixmap_t *pxm, + gfx_map_mask_t map) +{ + + + if (src.x < 0 || src.y < 0) { + SDLERROR("Attempt to grab pixmap from invalid coordinates (%d,%d)\n", src.x, src.y); + return GFX_ERROR; + } + + if (!pxm->data) { + SDLERROR("Attempt to grab pixmap to unallocated memory\n"); + return GFX_ERROR; + } + switch (map) { + + case GFX_MASK_VISUAL: { + SDL_Rect srect, drect; + SDL_Surface *temp; + + pxm->xl = src.xl; + pxm->yl = src.yl; + temp = SDL_CreateRGBSurface(SDL_SWSURFACE, src.xl, src.yl, + S->used_bytespp << 3, + S->primary->format->Rmask, + S->primary->format->Gmask, + S->primary->format->Bmask, + S->alpha_mask); + + if (!temp) { + SDLERROR("Failed to allocate SDL surface"); + return GFX_ERROR; + } + + if (SDL_MUSTLOCK(temp)) + sciprintf("Warning: SDL surface for pixmap grabbing requires locking\n"); + + if (ALPHASURFACE) + SDL_SetAlpha(temp, SDL_SRCALPHA,SDL_ALPHA_OPAQUE); + + srect.x = src.x; + srect.y = src.y; + srect.w = src.xl; + srect.h = src.yl; + drect.x = 0; + drect.y = 0; + drect.w = src.xl; + drect.h = src.yl; + + if (sdl_blit_surface(drv, S->visual[1], &srect, temp, &drect)) + SDLERROR("grab_pixmap: grab blit failed!\n"); + + pxm->internal.info = temp; + pxm->internal.handle = SCI_SDL_HANDLE_GRABBED; + pxm->flags |= GFX_PIXMAP_FLAG_INSTALLED | GFX_PIXMAP_FLAG_EXTERNAL_PALETTE | GFX_PIXMAP_FLAG_PALETTE_SET; + free(pxm->data); + pxm->data = (byte *) temp->pixels; + + DEBUGPXM("Grabbed surface %p (%dx%d)(%dx%d)\n", + pxm->internal.info, srect.x, srect.y, pxm->xl, pxm->yl); + + break; + } + + case GFX_MASK_PRIORITY: + SDLERROR("FIXME: priority map grab not implemented yet!\n"); + break; + + default: + SDLERROR("Attempt to grab pixmap from invalid map 0x%02x\n", map); + return GFX_ERROR; + } + + return GFX_OK; +} + + + /*** Buffer operations ***/ + +static int +sdl_update(struct _gfx_driver *drv, rect_t src, point_t dest, gfx_buffer_t buffer) +{ + int data_source = (buffer == GFX_BUFFER_BACK)? 2 : 1; + int data_dest = data_source - 1; + SDL_Rect srect, drect; + + if (src.x != dest.x || src.y != dest.y) { + DEBUGU("Updating %d (%d,%d)(%dx%d) to (%d,%d) on %d\n", buffer, src.x, src.y, + src.xl, src.yl, dest.x, dest.y, data_dest); + } else { + DEBUGU("Updating %d (%d,%d)(%dx%d) to %d\n", buffer, src.x, src.y, src.xl, src.yl, data_dest); + } + + srect.x = src.x; + srect.y = src.y; + srect.w = src.xl; + srect.h = src.yl; + drect.x = dest.x; + drect.y = dest.y; + drect.w = src.xl; + drect.h = src.yl; + + switch (buffer) { + case GFX_BUFFER_BACK: + if (sdl_blit_surface(drv, S->visual[data_source], &srect, + S->visual[data_dest], &drect)) + SDLERROR("surface update failed!\n"); + + if ((src.x == dest.x) && (src.y == dest.y)) + gfx_copy_pixmap_box_i(S->priority[0], S->priority[1], src); + break; + case GFX_BUFFER_FRONT: + if (sdl_blit_surface(drv, S->visual[data_source], &srect, S->primary, &drect)) + SDLERROR("primary surface update failed!\n"); + SDL_UpdateRect(S->primary, drect.x, drect.y, drect.w, drect.h); + break; + default: + GFXERROR("Invalid buffer %d in update!\n", buffer); + return GFX_ERROR; + } + + return GFX_OK; +} + +static int +sdl_set_static_buffer(struct _gfx_driver *drv, gfx_pixmap_t *pic, gfx_pixmap_t *priority) +{ + + if (!pic->internal.info) { + SDLERROR("Attempt to set static buffer with unregisterd pixmap!\n"); + return GFX_ERROR; + } + sdl_blit_surface(drv, (SDL_Surface *)pic->internal.info, NULL, + S->visual[2], NULL); + + gfx_copy_pixmap_box_i(S->priority[1], priority, gfx_rect(0, 0, 320*XFACT, 200*YFACT)); + + return GFX_OK; +} + + /*** Palette operations ***/ + +static int +sdl_set_palette(struct _gfx_driver *drv, int index, byte red, byte green, byte blue) +{ + if (index < 0 || index > 255) + { + SDLERROR("Attempt to set invalid palette entry %d\n", index); + return GFX_ERROR; + } + + S->colors[index].r = red; + S->colors[index].g = green; + S->colors[index].b = blue; + + SDL_SetColors(S->primary, S->colors + index, index, 1); + return GFX_OK; +} + + + /*** Mouse pointer operations ***/ + +byte * +sdl_create_cursor_rawdata(gfx_driver_t *drv, gfx_pixmap_t *pointer, int mode) +{ + int linewidth = (pointer->xl + 7) >> 3; + int lines = pointer->yl; + int xc, yc; + byte *data = (byte*)sci_calloc(linewidth, lines); + byte *linebase = data, *pos; + byte *src = pointer->index_data; + + for (yc = 0; yc < pointer->index_yl; yc++) { + int scalectr; + int bitc = 7; + pos = linebase; + + for (xc = 0; xc < pointer->index_xl; xc++) { + int draw = mode ? (*src == 0) : (*src < 255); + for (scalectr = 0; scalectr < XFACT; scalectr++) { + if (draw) + *pos |= (1 << bitc); + bitc--; + if (bitc < 0) { + bitc = 7; + pos++; + } + } + src++; + } + for (scalectr = 1; scalectr < YFACT; scalectr++) + memcpy(linebase + linewidth * scalectr, linebase, linewidth); + linebase += linewidth * YFACT; + } + return data; +} + + +static SDL_Cursor +*sdl_create_cursor_data(gfx_driver_t *drv, gfx_pixmap_t *pointer) +{ + byte *visual_data, *mask_data; + + S->pointer_data[0] = visual_data = sdl_create_cursor_rawdata(drv, pointer, 1); + S->pointer_data[1] = mask_data = sdl_create_cursor_rawdata(drv, pointer, 0); + + return SDL_CreateCursor(visual_data, mask_data, + pointer->xl, pointer->yl, + pointer->xoffset, pointer->yoffset); + +} + +static int sdl_set_pointer (struct _gfx_driver *drv, gfx_pixmap_t *pointer) +{ + int i; + + if (pointer == NULL) + SDL_ShowCursor(SDL_DISABLE); + else { + SDL_Cursor *cursor; + for (i = 0; i < 2; i++) + if (S->pointer_data[i]) { + free(S->pointer_data[i]); + S->pointer_data[i] = NULL; + } + + cursor = SDL_GetCursor(); + SDL_SetCursor(sdl_create_cursor_data(drv, pointer)); + SDL_FreeCursor(cursor); + SDL_ShowCursor(SDL_ENABLE); + } + + return 0; +} + +/*** Event management ***/ + +int +sdl_map_key(gfx_driver_t *drv, SDL_keysym keysym) +{ + SDLKey skey = keysym.sym; + int rkey = keysym.unicode & 0x7f; + + if ((skey >= SDLK_a) && (skey <= SDLK_z)) + return ('a' + (skey - SDLK_a)); + + if ((skey >= SDLK_0) && (skey <= SDLK_9)) + return ('0' + (skey - SDLK_0)); + + if (flags & SCI_SDL_SWAP_CTRL_CAPS) { + switch (skey) { + case SDLK_LCTRL: skey = SDLK_CAPSLOCK; break; + case SDLK_CAPSLOCK: skey = SDLK_LCTRL; break; + default: break; + } + } + + switch (skey) { + /* XXXX catch KMOD_NUM for KP0-9 */ + case SDLK_BACKSPACE: return SCI_K_BACKSPACE; + case SDLK_TAB: return 9; + case SDLK_ESCAPE: return SCI_K_ESC; + case SDLK_RETURN: + case SDLK_KP_ENTER: + if (SDL_GetModState() & KMOD_ALT) { + toggle_fullscreen(drv); + return 0; + } + return SCI_K_ENTER; + case SDLK_KP_PERIOD: return SCI_K_DELETE; + case SDLK_KP0: + case SDLK_INSERT: return SCI_K_INSERT; + case SDLK_KP1: + case SDLK_END: return SCI_K_END; + case SDLK_KP2: + case SDLK_DOWN: return SCI_K_DOWN; + case SDLK_KP3: + case SDLK_PAGEDOWN: return SCI_K_PGDOWN; + case SDLK_KP4: + case SDLK_LEFT: return SCI_K_LEFT; + case SDLK_KP5: return SCI_K_CENTER; + case SDLK_KP6: + case SDLK_RIGHT: return SCI_K_RIGHT; + case SDLK_KP7: + case SDLK_HOME: return SCI_K_HOME; + case SDLK_KP8: + case SDLK_UP: return SCI_K_UP; + case SDLK_KP9: + case SDLK_PAGEUP: return SCI_K_PGUP; + + case SDLK_F1: return SCI_K_F1; + case SDLK_F2: return SCI_K_F2; + case SDLK_F3: return SCI_K_F3; + case SDLK_F4: return SCI_K_F4; + case SDLK_F5: return SCI_K_F5; + case SDLK_F6: return SCI_K_F6; + case SDLK_F7: return SCI_K_F7; + case SDLK_F8: return SCI_K_F8; + case SDLK_F9: return SCI_K_F9; + case SDLK_F10: return SCI_K_F10; + + case SDLK_LCTRL: + case SDLK_RCTRL: + case SDLK_LALT: + case SDLK_RALT: + case SDLK_LMETA: + case SDLK_RMETA: + case SDLK_CAPSLOCK: + case SDLK_SCROLLOCK: + case SDLK_NUMLOCK: + case SDLK_LSHIFT: + case SDLK_RSHIFT: return 0; + + case SDLK_PLUS: + case SDLK_KP_PLUS: return '+'; + case SDLK_SLASH: + case SDLK_KP_DIVIDE: return '/'; + case SDLK_MINUS: + case SDLK_KP_MINUS: return '-'; + case SDLK_ASTERISK: + case SDLK_KP_MULTIPLY: return '*'; + case SDLK_EQUALS: + case SDLK_KP_EQUALS: return '='; + + case SDLK_COMMA: + case SDLK_PERIOD: + case SDLK_BACKSLASH: + case SDLK_SEMICOLON: + case SDLK_QUOTE: + case SDLK_LEFTBRACKET: + case SDLK_RIGHTBRACKET: + case SDLK_LESS: + case SDLK_DOLLAR: + case SDLK_GREATER: return rkey; + case SDLK_SPACE: return ' '; + +#ifdef MACOSX + case SDLK_WORLD_0: +#endif + case SDLK_BACKQUOTE: + if (keysym.mod & KMOD_CTRL) + return '`'; + else + return rkey; + + default: + break; + } + + sciprintf("Unknown SDL keysym: %04x (%d) \n", skey, rkey); + return 0; +} + + +void +sdl_fetch_event(gfx_driver_t *drv, sci_event_t *sci_event) +{ + SDL_Event event; + + while (SDL_PollEvent(&event)) { + + switch (event.type) { + case SDL_KEYDOWN: { + int modifiers = event.key.keysym.mod; + sci_event->type = SCI_EVT_KEYBOARD; + + S->buckystate = (((modifiers & KMOD_CAPS)? SCI_EVM_LSHIFT | SCI_EVM_RSHIFT : 0) + | ((modifiers & KMOD_CTRL)? SCI_EVM_CTRL : 0) + | ((modifiers & KMOD_ALT)? SCI_EVM_ALT : 0) + | ((modifiers & KMOD_NUM) ? SCI_EVM_NUMLOCK : 0) + | ((modifiers & KMOD_RSHIFT)? SCI_EVM_RSHIFT : 0) + | ((modifiers & KMOD_LSHIFT)? SCI_EVM_LSHIFT : 0)); + + sci_event->buckybits = S->buckystate; + sci_event->data = sdl_map_key(drv, event.key.keysym); + if (sci_event->data) + return; + break; + } + case SDL_MOUSEBUTTONDOWN: + sci_event->type = SCI_EVT_MOUSE_PRESS; + sci_event->buckybits = S->buckystate; + sci_event->data = event.button.button - 1; + drv->pointer_x = event.button.x; + drv->pointer_y = event.button.y; + return; + case SDL_MOUSEBUTTONUP: + sci_event->type = SCI_EVT_MOUSE_RELEASE; + sci_event->buckybits = S->buckystate; + sci_event->data = event.button.button - 1; + drv->pointer_x = event.button.x; + drv->pointer_y = event.button.y; + return; + case SDL_MOUSEMOTION: + drv->pointer_x = event.motion.x; + drv->pointer_y = event.motion.y; + break; + case SDL_QUIT: + sci_event->type = SCI_EVT_QUIT; + return; + break; + case SDL_VIDEOEXPOSE: + break; + default: + SDLERROR("Received unhandled SDL event %04x\n", event.type); + } + } + + sci_event->type = SCI_EVT_NONE; /* No event. */ +} + +static sci_event_t +sdl_get_event(struct _gfx_driver *drv) +{ + sci_event_t input; + + sdl_fetch_event(drv, &input); + return input; +} + +static int +sdl_usec_sleep(struct _gfx_driver *drv, long usecs) +{ + int msecs; + SDL_Event event; + + /* Wait at most 10ms to keep mouse cursor responsive. */ + msecs = usecs / 1000; + if (msecs > 10) + msecs = 10; + + SDL_PumpEvents(); + while (SDL_PeepEvents(&event, 1, SDL_GETEVENT, + SDL_EVENTMASK(SDL_MOUSEMOTION)) == 1) { + drv->pointer_x = event.motion.x; + drv->pointer_y = event.motion.y; + } + + SDL_Delay(msecs); + + return GFX_OK; +} + +gfx_driver_t +gfx_driver_sdl = { + "sdl", + "0.3a", + SCI_GFX_DRIVER_MAGIC, + SCI_GFX_DRIVER_VERSION, + NULL, + 0, 0, + GFX_CAPABILITY_MOUSE_SUPPORT | GFX_CAPABILITY_MOUSE_POINTER + | GFX_CAPABILITY_PIXMAP_REGISTRY | GFX_CAPABILITY_FINE_LINES, + 0, /*GFX_DEBUG_POINTER | GFX_DEBUG_UPDATES | GFX_DEBUG_PIXMAPS | GFX_DEBUG_BASIC, */ + sdl_set_parameter, + sdl_init_specific, + sdl_init, + sdl_exit, + sdl_draw_line, + sdl_draw_filled_rect, + sdl_register_pixmap, + sdl_unregister_pixmap, + sdl_draw_pixmap, + sdl_grab_pixmap, + sdl_update, + sdl_set_static_buffer, + sdl_set_pointer, + sdl_set_palette, + sdl_get_event, + sdl_usec_sleep, + NULL +}; + +#endif /* HAVE_SDL */ + + +/* reset to original optimisations for Win32: */ +/* (does not reset intrinsics) */ +#ifdef _WIN32 +//#pragma optimize( "", on ) +#endif diff --git a/engines/sci/gfx/drivers/xlib_driver.c b/engines/sci/gfx/drivers/xlib_driver.c new file mode 100644 index 0000000000..baa65d406b --- /dev/null +++ b/engines/sci/gfx/drivers/xlib_driver.c @@ -0,0 +1,1460 @@ +/*************************************************************************** + xlib_driver.h Copyright (C) 2000 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) <creichen@gmail.com> + +***************************************************************************/ + +#include <sci_memory.h> +#include <gfx_driver.h> +#ifndef X_DISPLAY_MISSING +#include <gfx_tools.h> + +#include <X11/Xlib.h> +#include <X11/Xutil.h> +#include <X11/Xos.h> +#include <X11/Xatom.h> +#include <X11/keysym.h> + +#ifdef HAVE_MITSHM +#include <sys/ipc.h> +#include <sys/shm.h> +#include <X11/extensions/XShm.h> +#if defined(HAVE_X11_EXTENSIONS_XRENDER_H) +# define HAVE_RENDER +# include <X11/extensions/Xrender.h> +# if defined(HAVE_X11_XFT_XFT_H) +# include <X11/Xft/Xft.h> +# endif +#endif +#include <errno.h> +#endif + +#ifdef HAVE_XM_MWMUTIL_H +#include <Xm/MwmUtil.h> +#endif + +#define SCI_XLIB_PIXMAP_HANDLE_NORMAL 0 +#define SCI_XLIB_PIXMAP_HANDLE_GRABBED 1 + +#define SCI_XLIB_SWAP_CTRL_CAPS (1 << 0) +#define SCI_XLIB_INSERT_MODE (1 << 1) +#define SCI_XLIB_NLS (1 << 2) /* Non-US keyboard support, sacrificing shortcut keys */ + +#define X_COLOR_EXT(c) ((c << 8) | c) + +/* In C++ mode, we re-name ``class'' to ``class_''. However, this screws up +** our interface to xlib, so we're not doing it in here. +*/ +#ifdef class +# undef class +#endif + +static int flags; + +struct _xlib_state { + Display *display; + Window window; + GC gc; + XGCValues gc_values; + Colormap colormap; + Pixmap visual[3]; + gfx_pixmap_t *priority[2]; +#ifdef HAVE_MITSHM + XShmSegmentInfo *shm[4]; +#endif + int use_render; + int buckystate; + XErrorHandler old_error_handler; + Cursor mouse_cursor; + byte *pointer_data[2]; + int used_bytespp; /* bytes actually used to display stuff, rather than bytes occupied in data space */ + XVisualInfo visinfo; +#ifdef HAVE_RENDER + Picture picture; +#endif +}; + +#define VISUAL S->visinfo.visual + +#define S ((struct _xlib_state *)(drv->state)) + +#define XASS(foo) { int val = foo; if (!val) xlderror(drv, __LINE__); } +#define XFACT drv->mode->xfact +#define YFACT drv->mode->yfact + +#define DEBUGB if (drv->debug_flags & GFX_DEBUG_BASIC && ((debugline = __LINE__))) xldprintf +#define DEBUGU if (drv->debug_flags & GFX_DEBUG_UPDATES && ((debugline = __LINE__))) xldprintf +#define DEBUGPXM if (drv->debug_flags & GFX_DEBUG_PIXMAPS && ((debugline = __LINE__))) xldprintf +#define DEBUGPTR if (drv->debug_flags & GFX_DEBUG_POINTER && ((debugline = __LINE__))) xldprintf +#define ERROR if ((debugline = __LINE__)) xldprintf + +#ifdef HAVE_MITSHM + +XShmSegmentInfo shminfo; +int have_shmem = 0; +int x11_error = 0; + +static int check_for_xshm(Display *display) +{ + int major, minor, ignore; + Bool pixmaps; + if (XQueryExtension(display, "MIT-SHM", &ignore, &ignore, &ignore)) { + if (XShmQueryVersion( display, &major, &minor, &pixmaps) == True) { + return (pixmaps == True) ? 2 : 1 ; + } else { + return 0; + } + } + return 0; +} +#endif + +#ifdef HAVE_RENDER +static int x_have_render(Display *display) +{ + int ignore, retval; + + printf("Checking for X11 RENDER extension:"); + + retval = XQueryExtension(display, "RENDER", &ignore, &ignore, &ignore); + + if (retval) + printf(" found.\n"); + else + printf(" not found.\n"); + + return retval; +} +#endif + +static int debugline = 0; + +static void +xldprintf(const char *fmt, ...) +{ + va_list argp; + fprintf(stderr,"GFX-XLIB %d:", debugline); + va_start(argp, fmt); + vfprintf(stderr, fmt, argp); + va_end(argp); +} + +static void +xlderror(gfx_driver_t *drv, int line) +{ + xldprintf("Xlib Error in line %d\n", line); +} + +static unsigned long +xlib_map_color(gfx_driver_t *drv, gfx_color_t color) +{ + gfx_mode_t *mode = drv->mode; + unsigned long temp; + unsigned long retval = 0; + + if (drv->mode->palette) + return color.visual.global_index; + + temp = color.visual.r; + temp |= temp << 8; + temp |= temp << 16; + retval |= (temp >> mode->red_shift) & (mode->red_mask); + temp = color.visual.g; + temp |= temp << 8; + temp |= temp << 16; + retval |= (temp >> mode->green_shift) & (mode->green_mask); + temp = color.visual.b; + temp |= temp << 8; + temp |= temp << 16; + retval |= (temp >> mode->blue_shift) & (mode->blue_mask); + + return retval; +} + + +static int +xlib_error_handler(Display *display, XErrorEvent *error) +{ + char errormsg[256]; +#ifdef HAVE_MITSHM + x11_error = 1; +#endif + XGetErrorText(display, error->error_code, errormsg, 255); + ERROR(" X11: %s\n", errormsg); + return 0; +} + +#define UPDATE_NLS_CAPABILITY \ + if (flags & SCI_XLIB_NLS) \ + drv->capabilities |= GFX_CAPABILITY_KEYTRANSLATE; \ + else \ + drv->capabilities &= ~GFX_CAPABILITY_KEYTRANSLATE + + + +static int +xlib_set_parameter(struct _gfx_driver *drv, char *attribute, char *value) +{ + if (!strncmp(attribute, "swap_ctrl_caps",17) || + !strncmp(attribute, "swap_caps_ctrl",17)) { + if (string_truep(value)) + flags |= SCI_XLIB_SWAP_CTRL_CAPS; + else + flags &= ~SCI_XLIB_SWAP_CTRL_CAPS; + + + return GFX_OK; + } + + if (!strncmp(attribute, "localised_keyboard", 18) + || !strncmp(attribute, "localized_keyboard", 18)) { + if (string_truep(value)) + flags |= SCI_XLIB_NLS; + else + flags &= ~SCI_XLIB_NLS; + + UPDATE_NLS_CAPABILITY; + + return GFX_OK; + + } + + if (!strncmp(attribute, "disable_shmem", 14)) { +#ifdef HAVE_MITSHM + if (string_truep(value)) + have_shmem = -1; +#endif + return GFX_OK; + } + + ERROR("Attempt to set xlib parameter \"%s\" to \"%s\"\n", attribute, value); + return GFX_ERROR; +} + +Cursor +x_empty_cursor(Display *display, Drawable drawable) /* Generates an empty X cursor */ +{ + byte cursor_data[] = {0}; + XColor black = {0,0,0}; + Pixmap cursor_map; + + Cursor retval; + + cursor_map = XCreateBitmapFromData(display, drawable, (char *) cursor_data, 1, 1); + + retval = XCreatePixmapCursor(display, cursor_map, cursor_map, &black, &black, 0, 0); + + XFreePixmap(display, cursor_map); + + return retval; +} + +static int +xlib_draw_filled_rect(struct _gfx_driver *drv, rect_t rect, + gfx_color_t color1, gfx_color_t color2, + gfx_rectangle_fill_t shade_mode); +static int +xlib_draw_pixmap(struct _gfx_driver *drv, gfx_pixmap_t *pxm, int priority, + rect_t src, rect_t dest, gfx_buffer_t buffer); + + +static int +xlib_init_specific(struct _gfx_driver *drv, int xfact, int yfact, int bytespp) +{ + XVisualInfo xvisinfo; + XSetWindowAttributes win_attr; + int default_screen; + int vistype = (bytespp == 1)? 3 /* PseudoColor */ : 4 /* TrueColor */; + int red_shift, green_shift, blue_shift, alpha_shift; + int bytespp_physical; + int depth_mod; /* Number of bits to subtract from the depth while checking */ + unsigned int alpha_mask; + int xsize, ysize; + XSizeHints *size_hints; + XClassHint *class_hint; + XImage *foo_image = NULL; + int reverse_endian = 0; +#ifdef HAVE_XM_MWMUTIL_H + PropMotifWmHints motif_hints; + Atom prop, proptype; +#endif + + int i; + + + UPDATE_NLS_CAPABILITY; + + if (!drv->state /* = S */) + drv->state = sci_malloc(sizeof(struct _xlib_state)); + + flags |= SCI_XLIB_INSERT_MODE; + + S->display = XOpenDisplay(NULL); + + if (!S->display) { + ERROR("Could not open X connection!\n"); + return GFX_FATAL; + } + + default_screen = DefaultScreen(S->display); + + if (xfact == -1 && yfact == -1) { /* Detect (used INTERNALLY!) */ + xfact = 2; + if (DisplayWidth(S->display, default_screen) < 640 + || DisplayHeight(S->display, default_screen) < 400) + xfact = 1; + + yfact = xfact; + } + + xsize = xfact * 320; + ysize = yfact * 200; + if (xfact < 1 || yfact < 1 || bytespp < 1 || bytespp > 4) { + ERROR("Internal error: Attempt to open window w/ scale factors (%d,%d) and bpp=%d!\n", + xfact, yfact, bytespp); + BREAKPOINT(); + } + +#ifdef HAVE_MITSHM + if (!have_shmem) { + have_shmem = check_for_xshm(S->display); + if (have_shmem) { + printf("Using the MIT-SHM extension (%d/%d)\n", have_shmem, + XShmPixmapFormat(S->display)); + } + memset(&shminfo, 0, sizeof(XShmSegmentInfo)); + } + for (i = 0; i < 4; i++) + S->shm[i] = NULL; +#endif + + depth_mod = 0; + + /* Try all bit size twiddling */ + for (depth_mod = 0; depth_mod < 8; depth_mod++) + /* Try all viable depths */ + for (; bytespp > 0; bytespp--) + /* Try all interesting visuals */ + for (vistype = 4; vistype >= 3; vistype--) + if (XMatchVisualInfo(S->display, default_screen, + (bytespp << 3) - depth_mod, vistype, &xvisinfo)) + goto found_visual; + found_visual: + + S->visinfo = xvisinfo; + + if (vistype < 3 || ((vistype == 3) && (bytespp != 1))) { + if (bytespp == 1) { + ERROR("Could not get an 8 bit Pseudocolor visual!\n"); + } else { + ERROR("Could not get a %d bit TrueColor visual!\n", bytespp << 3); + } + return GFX_FATAL; + } + + S->colormap = win_attr.colormap = + XCreateColormap(S->display, RootWindow(S->display, default_screen), + VISUAL, (bytespp == 1)? AllocAll : AllocNone); + + win_attr.event_mask = PointerMotionMask | StructureNotifyMask | ButtonPressMask + | ButtonReleaseMask | KeyPressMask | KeyReleaseMask | ExposureMask; + win_attr.background_pixel = win_attr.border_pixel = 0; + + S->window = XCreateWindow(S->display, RootWindow(S->display, default_screen), + 0, 0, xsize, ysize, 0, xvisinfo.depth, InputOutput, + VISUAL, (CWBackPixel | CWBorderPixel | CWColormap | CWEventMask), + &win_attr); + + if (!S->window) { + ERROR("Could not create window of size %dx%d!\n", 320*xfact, 200*yfact); + return GFX_FATAL; + } + + XSync(S->display, False); +#ifdef HAVE_XM_MWMUTIL_H + motif_hints.flags = MWM_HINTS_DECORATIONS|MWM_HINTS_FUNCTIONS; + motif_hints.decorations = MWM_DECOR_BORDER|MWM_DECOR_TITLE|MWM_DECOR_MENU|MWM_DECOR_MINIMIZE; + motif_hints.functions=0 +#ifdef MWM_FUNC_MOVE + | MWM_FUNC_MOVE +#endif +#ifdef MWM_FUNC_MINIMIZE + | MWM_FUNC_MINIMIZE +#endif +#ifdef MWM_FUNC_CLOSE + | MWM_FUNC_CLOSE +#endif +#ifdef MWM_FUNC_QUIT_APP + | MWM_FUNC_QUIT_APP +#endif + ; + + prop = XInternAtom(S->display, "_MOTIF_WM_HINTS", True ); + if (prop) { + proptype = prop; + XChangeProperty(S->display, S->window, prop, proptype, 32, PropModeReplace, (unsigned char *) &motif_hints, PROP_MOTIF_WM_HINTS_ELEMENTS); + } +#endif + + XStoreName(S->display, S->window, "FreeSCI"); + XDefineCursor(S->display, S->window, (S->mouse_cursor = x_empty_cursor(S->display, S->window))); + + XMapWindow(S->display, S->window); + S->buckystate = 0; + + if (bytespp == 1) + red_shift = green_shift = blue_shift = 0; + else { + red_shift = 32 - ffs((~xvisinfo.red_mask >> 1) & (xvisinfo.red_mask)); + green_shift = 32 - ffs((~xvisinfo.green_mask >> 1) & (xvisinfo.green_mask)); + blue_shift = 32 - ffs((~xvisinfo.blue_mask >> 1) & (xvisinfo.blue_mask)); + } + + class_hint = XAllocClassHint(); + class_hint->res_name = "FreeSCI"; + class_hint->res_class = "FreeSCI"; + XSetIconName(S->display, S->window, "FreeSCI"); + XSetClassHint(S->display, S->window, class_hint); + XFree(class_hint); + size_hints = XAllocSizeHints(); + size_hints->base_width = size_hints->min_width = size_hints->max_width = xsize; + size_hints->base_height = size_hints->min_height = size_hints->max_height = ysize; + size_hints->flags |= PMinSize | PMaxSize | PBaseSize; + XSetWMNormalHints(S->display, S->window, size_hints); + XFree(size_hints); + + S->gc_values.foreground = BlackPixel(S->display, DefaultScreen(S->display)); + S->gc = XCreateGC(S->display, S->window, GCForeground, &(S->gc_values)); + + for (i = 0; i < 2; i++) { + S->priority[i] = gfx_pixmap_alloc_index_data(gfx_new_pixmap(xsize, ysize, GFX_RESID_NONE, -i, -777)); + if (!S->priority[i]) { + ERROR("Out of memory: Could not allocate priority maps! (%dx%d)\n", + xsize, ysize); + return GFX_FATAL; + } + } + + foo_image = XCreateImage(S->display, VISUAL, + bytespp << 3, ZPixmap, 0, (char*)sci_malloc(23), 2, 2, 8, 0); + + bytespp_physical = foo_image->bits_per_pixel >> 3; +#ifdef WORDS_BIGENDIAN + reverse_endian = foo_image->byte_order == LSBFirst; +#else + reverse_endian = foo_image->byte_order == MSBFirst; +#endif + XDestroyImage(foo_image); + +#ifdef HAVE_MITSHM + /* set up and test the XSHM extension to make sure it's sane */ + if (have_shmem) { + XErrorHandler old_handler; + + x11_error = 0; + old_handler = XSetErrorHandler(xlib_error_handler); + + foo_image = XShmCreateImage(S->display, VISUAL, + bytespp_physical << 3, ZPixmap, 0, &shminfo, 2, 2); + if (foo_image) + shminfo.shmid = shmget(IPC_PRIVATE, + foo_image->bytes_per_line * + foo_image->height, + IPC_CREAT | 0777); + if (-1 == shminfo.shmid) { + have_shmem = 0; + ERROR("System does not support SysV IPC, disabling XSHM\n"); + perror("reason"); + foo_image = NULL; + } else { + + shminfo.shmaddr = (char *) shmat(shminfo.shmid, 0, 0); + if ((void *) -1 == shminfo.shmaddr) { + ERROR("Could not attach shared memory segment\n"); + perror("reason"); + if (foo_image) + XDestroyImage(foo_image); + return GFX_FATAL; + } + + foo_image->data = shminfo.shmaddr; + shminfo.readOnly = False; + + XShmAttach(S->display, &shminfo); + XSync(S->display, False); + shmctl(shminfo.shmid, IPC_RMID, 0); + + if (x11_error) { + have_shmem = 0; + ERROR("System does not support Shared XImages, disabling\n"); + shmdt(shminfo.shmaddr); + XDestroyImage(foo_image); + foo_image = NULL; + x11_error = 0; + } + XSetErrorHandler(old_handler); + } + } + +#endif + + + alpha_mask = xvisinfo.red_mask | xvisinfo.green_mask | xvisinfo.blue_mask; + if (!reverse_endian && bytespp_physical == 4 && (!(alpha_mask & 0xff000000) || !(alpha_mask & 0xff))) { + if (alpha_mask & 0xff) { + alpha_mask = 0xff000000; + alpha_shift = 0; + } else { /* Lowest byte */ + alpha_mask = 0x000000ff; + alpha_shift = 24; + } + } else { + alpha_mask = 0; + alpha_shift = 0; + } + /* create the visual buffers */ + for (i = 0; i < 3; i++) { +#ifdef HAVE_MITSHM + XErrorHandler old_handler; + + if (have_shmem && have_shmem != 2) { + ERROR("Shared memory pixmaps not supported. Reverting\n"); + perror("reason"); + have_shmem = 0; + } + + if (have_shmem) { + old_handler = XSetErrorHandler(xlib_error_handler); + + if ((S->shm[i] = (XShmSegmentInfo*)sci_malloc(sizeof(XShmSegmentInfo))) == 0) { + ERROR("AIEEEE! Malloc failed!\n"); + return GFX_FATAL; + } + memset(S->shm[i], 0, sizeof(XShmSegmentInfo)); + + S->shm[i]->shmid = shmget(IPC_PRIVATE, xsize * ysize * + bytespp_physical, + IPC_CREAT | IPC_EXCL | 0666); + S->shm[i]->readOnly = False; + + if (S->shm[i]->shmid == -1) { + have_shmem = 0; + ERROR("System does not support SysV IPC, disabling XSHM\n"); + perror("reason"); + } + } + if (have_shmem) { + S->shm[i]->shmaddr = (char *) shmat(S->shm[i]->shmid, 0, 0); + if (S->shm[i]->shmaddr == (void *) -1) { + ERROR("Could not attach shared memory segment\n"); + perror("reason"); + have_shmem = 0; + } + } + if (have_shmem) { + if (!XShmAttach(S->display, S->shm[i]) || x11_error) { + ERROR("ARGH! Can't attach shared memory segment\n"); + have_shmem = 0; + } + XSync(S->display, False); + shmctl(S->shm[i]->shmid, IPC_RMID, 0); + } + + if (have_shmem && !x11_error) { + S->visual[i] = XShmCreatePixmap(S->display, S->window, + S->shm[i]->shmaddr, + S->shm[i], xsize, ysize, + bytespp << 3); + XSync(S->display, False); + + if (x11_error || !S->visual[i]) { + ERROR("Shared Memory Pixmaps not supported on this system. Disabling!\n"); + have_shmem = 0; + XFreePixmap(S->display, S->visual[i]); + XShmDetach(S->display ,S->shm[i]); + XSync(S->display, False); + S->visual[i] = 0; + x11_error = 0; + shmdt(S->shm[i]->shmaddr); + sci_free(S->shm[i]); + + } + + } + XSetErrorHandler(old_handler); + + if (!have_shmem) +#endif + S->visual[i] = XCreatePixmap(S->display, S->window, xsize, ysize, bytespp << 3); + + XFillRectangle(S->display, S->visual[i], S->gc, 0, 0, xsize, ysize); + } + + /** X RENDER handling **/ +#ifdef HAVE_RENDER + S->use_render = x_have_render(S->display); + + if (S->use_render) { + XRenderPictFormat * format + = XRenderFindVisualFormat (S->display, + VISUAL); + S->picture = XRenderCreatePicture (S->display, + (Drawable) S->visual[1], + format, + 0, 0); + } else /* No Xrender */ + drv->draw_filled_rect = xlib_draw_filled_rect; +#else + S->use_render = 0; +#endif + /** End of X RENDER handling **/ + + + drv->mode = gfx_new_mode(xfact, yfact, bytespp_physical, + xvisinfo.red_mask, xvisinfo.green_mask, + xvisinfo.blue_mask, alpha_mask, + red_shift, green_shift, blue_shift, alpha_shift, + (bytespp == 1)? xvisinfo.colormap_size : 0, + (reverse_endian)? GFX_MODE_FLAG_REVERSE_ENDIAN : 0); + +#ifdef HAVE_MITSHM + if (have_shmem) { + XShmDetach(S->display, &shminfo); + XDestroyImage(foo_image); + shmdt(shminfo.shmaddr); + } +#endif + + S->used_bytespp = bytespp; + S->old_error_handler = (XErrorHandler) XSetErrorHandler(xlib_error_handler); + S->pointer_data[0] = NULL; + S->pointer_data[1] = NULL; + + + return GFX_OK; +} + + +static void +xlib_xdpy_info() +{ + int i; + XVisualInfo foo; + XVisualInfo *visuals; + int visuals_nr; + Display *display = XOpenDisplay(NULL); + const char *vis_classes[6] = {"StaticGray", "GrayScale", "StaticColor", + "PseudoColor", "TrueColor", "DirectColor"}; + + printf("Visuals provided by X11 server:\n"); + visuals = XGetVisualInfo(display, VisualNoMask, &foo, &visuals_nr); + + if (!visuals_nr) { + printf(" None!\n"); + } + + for (i = 0; i < visuals_nr; i++) { + XVisualInfo *visual = visuals + i; + + /* This works around an incompatibility between C++ and xlib: access visual->class */ + int visual_class = *((int *) (((byte *)(&(visual->depth))) + sizeof(unsigned int))); + + printf("%d:\t%d bpp %s(%d)\n" + "\tR:%08lx G:%08lx B:%08lx\n" + "\tbits_per_rgb=%d\n" + "\tcolormap_size=%d\n\n", + i, + visual->depth, + (visual_class < 0 || visual_class >5)? + "INVALID" : + vis_classes[visual_class], + visual_class, + visual->red_mask, visual->green_mask, visual->blue_mask, + visual->bits_per_rgb, visual->colormap_size); + + } + + if (visuals) + XFree(visuals); +} + +static int +xlib_init(struct _gfx_driver *drv) +{ + int i; + + /* Try 32-bit mode last due to compiz issue with bit depth 32. */ + for (i = 3; i > 0; i--) + if (!xlib_init_specific(drv, -1, -1, i)) + return GFX_OK; + + if (!xlib_init_specific(drv, -1, -1, 4)) + return GFX_OK; + + fprintf(stderr, "Could not find supported mode!\n"); + xlib_xdpy_info(); + + return GFX_FATAL; +} + +static void +xlib_exit(struct _gfx_driver *drv) +{ + int i; + if (S) { + for (i = 0; i < 2; i++) { + gfx_free_pixmap(drv, S->priority[i]); + S->priority[i] = NULL; + } + + for (i = 0; i < 3; i++) { +#ifdef HAVE_MITSHM + if (have_shmem && S->shm[i]) { + XFreePixmap(S->display, S->visual[i]); + XShmDetach(S->display, S->shm[i]); + + if (S->shm[i]->shmid >=0) + shmctl(S->shm[i]->shmid, IPC_RMID, 0); + if (S->shm[i]->shmaddr) + shmdt(S->shm[i]->shmaddr); + + sci_free(S->shm[i]); + S->shm[i] = NULL; + } else +#endif + XFreePixmap(S->display, S->visual[i]); + } + +#ifdef HAVE_RENDER + XRenderFreePicture(S->display, S->picture); +#endif + XFreeGC(S->display, S->gc); + XDestroyWindow(S->display, S->window); + XCloseDisplay(S->display); + XSetErrorHandler((XErrorHandler) (S->old_error_handler)); + sci_free(S); + drv->state /* = S */ = NULL; + gfx_free_mode(drv->mode); + } +} + + + /*** Drawing operations ***/ + +static int +xlib_draw_line(struct _gfx_driver *drv, point_t start, point_t end, gfx_color_t color, + gfx_line_mode_t line_mode, gfx_line_style_t line_style) +{ + int linewidth = (line_mode == GFX_LINE_MODE_FINE)? 1: + (drv->mode->xfact + drv->mode->yfact) >> 1; + + if (color.mask & GFX_MASK_VISUAL) { + int xmod = drv->mode->xfact >> 1; + int ymod = drv->mode->yfact >> 1; + + if (line_mode == GFX_LINE_MODE_FINE) + xmod = ymod = 0; + + S->gc_values.foreground = xlib_map_color(drv, color); + S->gc_values.line_width = linewidth; + S->gc_values.line_style = (line_style == GFX_LINE_STYLE_NORMAL)? + LineSolid : LineOnOffDash; + S->gc_values.cap_style = CapProjecting; + + XChangeGC(S->display, S->gc, GCLineWidth | GCLineStyle | GCForeground | GCCapStyle, &(S->gc_values)); + + XASS(XDrawLine(S->display, S->visual[1], S->gc, + start.x + xmod, start.y + ymod, + end.x + xmod, end.y + ymod)); + } + + if (color.mask & GFX_MASK_PRIORITY) { + int xc, yc; + point_t nstart, nend; + + linewidth--; + for (xc = 0; xc <= linewidth; xc++) + for (yc = -linewidth; yc <= linewidth; yc++) { + nstart.x = start.x + xc; + nstart.y = start.y + yc; + + nend.x = end.x + xc; + nend.y = end.y + yc; + + gfx_draw_line_pixmap_i(S->priority[0], + nstart, nend, color.priority); + } + } + + return GFX_OK; +} + +static int +xlib_draw_filled_rect(struct _gfx_driver *drv, rect_t rect, + gfx_color_t color1, gfx_color_t color2, + gfx_rectangle_fill_t shade_mode) +{ + if (color1.mask & GFX_MASK_VISUAL) { + S->gc_values.foreground = xlib_map_color(drv, color1); + XChangeGC(S->display, S->gc, GCForeground, &(S->gc_values)); + XASS(XFillRectangle(S->display, S->visual[1], S->gc, rect.x, rect.y, + rect.xl, rect.yl)); + } + + if (color1.mask & GFX_MASK_PRIORITY) + gfx_draw_box_pixmap_i(S->priority[0], rect, color1.priority); + + return GFX_OK; +} + +#ifdef HAVE_RENDER +static int +xlib_draw_filled_rect_RENDER(struct _gfx_driver *drv, rect_t rect, + gfx_color_t color1, gfx_color_t color2, + gfx_rectangle_fill_t shade_mode) +{ + if (S->used_bytespp == 1) /* No room for alpha! */ + return xlib_draw_filled_rect(drv, rect, color1, color2, shade_mode); + + if (color1.mask & GFX_MASK_VISUAL) { + XRenderColor fg; + + fg.red = X_COLOR_EXT(color1.visual.r); + fg.green = X_COLOR_EXT(color1.visual.g); + fg.blue = X_COLOR_EXT(color1.visual.b); + fg.alpha = 0xffff - X_COLOR_EXT(color1.alpha); + + + XRenderFillRectangle(S->display, + PictOpOver, + S->picture, + &fg, + rect.x, rect.y, + rect.xl, rect.yl); + } + + if (color1.mask & GFX_MASK_PRIORITY) + gfx_draw_box_pixmap_i(S->priority[0], rect, color1.priority); + + return GFX_OK; +} +#endif + + /*** Pixmap operations ***/ + +static int +xlib_register_pixmap(struct _gfx_driver *drv, gfx_pixmap_t *pxm) +{ + if (pxm->internal.info) { + ERROR("Attempt to register pixmap twice!\n"); + return GFX_ERROR; + } + pxm->internal.info = XCreateImage(S->display, VISUAL, + S->used_bytespp << 3, ZPixmap, 0, (char *) pxm->data, pxm->xl, + pxm->yl, 8, 0); + + DEBUGPXM("Registered pixmap %d/%d/%d at %p (%dx%d)\n", pxm->ID, pxm->loop, pxm->cel, + pxm->internal.info, pxm->xl, pxm->yl); + return GFX_OK; +} + +static int +xlib_unregister_pixmap(struct _gfx_driver *drv, gfx_pixmap_t *pxm) +{ + DEBUGPXM("Freeing pixmap %d/%d/%d at %p\n", pxm->ID, pxm->loop, pxm->cel, + pxm->internal.info); + + if (!pxm->internal.info) { + ERROR("Attempt to unregister pixmap twice!\n"); + return GFX_ERROR; + } + + XDestroyImage((XImage *) pxm->internal.info); + pxm->internal.info = NULL; + pxm->data = NULL; /* Freed by XDestroyImage */ + return GFX_OK; +} + +static int +xlib_draw_pixmap(struct _gfx_driver *drv, gfx_pixmap_t *pxm, int priority, + rect_t src, rect_t dest, gfx_buffer_t buffer) +{ + int bufnr = (buffer == GFX_BUFFER_STATIC)? 2:1; + int pribufnr = bufnr -1; + XImage *tempimg; + + if (dest.xl != src.xl || dest.yl != src.yl) { + ERROR("Attempt to scale pixmap (%dx%d)->(%dx%d): Not supported\n", + src.xl, src.yl, dest.xl, dest.yl); + return GFX_ERROR; + } + + if (pxm->internal.handle == SCI_XLIB_PIXMAP_HANDLE_GRABBED) { + XPutImage(S->display, S->visual[bufnr], S->gc, (XImage *) pxm->internal.info, + src.x, src.y, dest.x, dest.y, dest.xl, dest.yl); + return GFX_OK; + } + + tempimg = XGetImage(S->display, S->visual[bufnr], dest.x, dest.y, + dest.xl, dest.yl, 0xffffffff, ZPixmap); + + if (!tempimg) { + ERROR("Failed to grab X image!\n"); + return GFX_ERROR; + } + + gfx_crossblit_pixmap(drv->mode, pxm, priority, src, dest, + (byte *) tempimg->data, tempimg->bytes_per_line, + S->priority[pribufnr]->index_data, + S->priority[pribufnr]->index_xl, 1, + GFX_CROSSBLIT_FLAG_DATA_IS_HOMED); + + XPutImage(S->display, S->visual[bufnr], S->gc, tempimg, + 0, 0, dest.x, dest.y, dest.xl, dest.yl); + + XDestroyImage(tempimg); + return GFX_OK; +} + + +static int +xlib_grab_pixmap(struct _gfx_driver *drv, rect_t src, gfx_pixmap_t *pxm, + gfx_map_mask_t map) +{ + + if (src.x < 0 || src.y < 0) { + ERROR("Attempt to grab pixmap from invalid coordinates (%d,%d)\n", src.x, src.y); + return GFX_ERROR; + } + + if (!pxm->data) { + ERROR("Attempt to grab pixmap to unallocated memory\n"); + return GFX_ERROR; + } + + switch (map) { + + case GFX_MASK_VISUAL: + pxm->xl = src.xl; + pxm->yl = src.yl; + + pxm->internal.info = XGetImage(S->display, S->visual[1], src.x, src.y, + src.xl, src.yl, 0xffffffff, ZPixmap); + pxm->internal.handle = SCI_XLIB_PIXMAP_HANDLE_GRABBED; + pxm->flags |= GFX_PIXMAP_FLAG_INSTALLED | GFX_PIXMAP_FLAG_EXTERNAL_PALETTE | GFX_PIXMAP_FLAG_PALETTE_SET; + sci_free(pxm->data); + pxm->data = (byte *) ((XImage *)(pxm->internal.info))->data; + break; + + case GFX_MASK_PRIORITY: + ERROR("FIXME: priority map grab not implemented yet!\n"); + break; + + default: + ERROR("Attempt to grab pixmap from invalid map 0x%02x\n", map); + return GFX_ERROR; + } + + return GFX_OK; +} + + + /*** Buffer operations ***/ + +static int +xlib_update(struct _gfx_driver *drv, rect_t src, point_t dest, gfx_buffer_t buffer) +{ + int data_source = (buffer == GFX_BUFFER_BACK)? 2 : 1; + int data_dest = data_source - 1; + + + if (src.x != dest.x || src.y != dest.y) { + DEBUGU("Updating %d (%d,%d)(%dx%d) to (%d,%d)\n", buffer, src.x, src.y, + src.xl, src.yl, dest.x, dest.y); + } else { + DEBUGU("Updating %d (%d,%d)(%dx%d)\n", buffer, src.x, src.y, src.xl, src.yl); + } + + XCopyArea(S->display, S->visual[data_source], S->visual[data_dest], S->gc, + src.x, src.y, src.xl, src.yl, dest.x, dest.y); + + if (buffer == GFX_BUFFER_BACK && (src.x == dest.x) && (src.y == dest.y)) + gfx_copy_pixmap_box_i(S->priority[0], S->priority[1], src); + else { + gfx_color_t col; + col.mask = GFX_MASK_VISUAL; + col.visual.r = 0xff; + col.visual.g = 0; + col.visual.b = 0; + + XCopyArea(S->display, S->visual[0], S->window, S->gc, + dest.x, dest.y, src.xl, src.yl, dest.x, dest.y); + } + + return GFX_OK; +} + +static int +xlib_set_static_buffer(struct _gfx_driver *drv, gfx_pixmap_t *pic, gfx_pixmap_t *priority) +{ + + if (!pic->internal.info) { + ERROR("Attempt to set static buffer with unregisterd pixmap!\n"); + return GFX_ERROR; + } + XPutImage(S->display, S->visual[2], S->gc, (XImage *) pic->internal.info, + 0, 0, 0, 0, 320 * XFACT, 200 * YFACT); + gfx_copy_pixmap_box_i(S->priority[1], priority, gfx_rect(0, 0, 320*XFACT, 200*YFACT)); + + return GFX_OK; +} + + + /*** Mouse pointer operations ***/ + +byte * +xlib_create_cursor_data(gfx_driver_t *drv, gfx_pixmap_t *pointer, int mode) +{ + int linewidth = (pointer->xl + 7) >> 3; + int lines = pointer->yl; + int xc, yc; + int xfact = drv->mode->xfact; + byte *data = (byte*)sci_calloc(linewidth, lines); + byte *linebase = data, *pos; + byte *src = pointer->index_data; + + + for (yc = 0; yc < pointer->index_yl; yc++) { + int scalectr; + int bitc = 0; + pos = linebase; + + + for (xc = 0; xc < pointer->index_xl; xc++) { + int draw = mode? + (*src == 0) : (*src < 255); + + for (scalectr = 0; scalectr < xfact; scalectr++) { + if (draw) + *pos |= (1 << bitc); + + bitc++; + if (bitc == 8) { + bitc = 0; + pos++; + } + } + + src++; + } + for (scalectr = 1; scalectr < drv->mode->yfact; scalectr++) + memcpy(linebase + linewidth * scalectr, linebase, linewidth); + + linebase += linewidth * drv->mode->yfact; + } + + return data; +} + +static int +xlib_set_pointer(struct _gfx_driver *drv, gfx_pixmap_t *pointer) +{ + int i; + XFreeCursor(S->display, S->mouse_cursor); + + for (i = 0; i < 2; i++) + if (S->pointer_data[i]) { + sci_free(S->pointer_data[i]); + S->pointer_data[i] = NULL; + } + + if (pointer == NULL) + S->mouse_cursor = x_empty_cursor(S->display, S->window); + else { + XColor cols[2]; + Pixmap visual, mask; + byte *mask_data, *visual_data; + int real_xl = ((pointer->xl + 7) >> 3) << 3; + int i; + + for (i = 0; i < 2; i++) { + cols[i].red = pointer->colors[i].r; + cols[i].red |= (cols[i].red << 8); + cols[i].green = pointer->colors[i].g; + cols[i].green |= (cols[i].green << 8); + cols[i].blue = pointer->colors[i].b; + cols[i].blue |= (cols[i].blue << 8); + } + + S->pointer_data[0] = visual_data = xlib_create_cursor_data(drv, pointer, 1); + S->pointer_data[1] = mask_data = xlib_create_cursor_data(drv, pointer, 0); + S->pointer_data[0] = NULL; + S->pointer_data[1] = NULL; + visual = XCreateBitmapFromData(S->display, S->window, (char *) visual_data, real_xl, pointer->yl); + mask = XCreateBitmapFromData(S->display, S->window, (char *) mask_data, real_xl, pointer->yl); + + + S->mouse_cursor = + XCreatePixmapCursor(S->display, visual, mask, + &(cols[0]), &(cols[1]), + pointer->xoffset, pointer->yoffset); + + XFreePixmap(S->display, visual); + XFreePixmap(S->display, mask); + sci_free(mask_data); + sci_free(visual_data); + } + + XDefineCursor(S->display, S->window, S->mouse_cursor); + + return 0; +} + + + /*** Palette operations ***/ + +static int +xlib_set_palette(struct _gfx_driver *drv, int index, byte red, byte green, byte blue) +{ + char stringbuf[8]; + sprintf(stringbuf, "#%02x%02x%02x", red, green, blue); /* Argh. */ + + XStoreNamedColor(S->display, S->colormap, stringbuf, index, DoRed | DoGreen | DoBlue); + /* Isn't there some way to do this without strings? */ + + return GFX_OK; +} + + + /*** Event management ***/ +/* +int +x_unmap_key(gfx_driver_t *drv, int keycode) +{ + KeySym xkey = XKeycodeToKeysym(S->display, keycode, 0); + + return 0; +} +*/ +int +x_map_key(gfx_driver_t *drv, XEvent *key_event, char *character) +{ + KeySym xkey = XKeycodeToKeysym(S->display, key_event->xkey.keycode, 0); + + *character = 0; + + if (flags & SCI_XLIB_SWAP_CTRL_CAPS) { + switch (xkey) { + case XK_Control_L: xkey = XK_Caps_Lock; break; + case XK_Caps_Lock: xkey = XK_Control_L; break; + } + } + + switch(xkey) { + + case XK_BackSpace: return SCI_K_BACKSPACE; + case XK_Tab: return 9; + case XK_Escape: return SCI_K_ESC; + case XK_Return: + case XK_KP_Enter: return SCI_K_ENTER; + + case XK_KP_Decimal: + case XK_KP_Delete: return SCI_K_DELETE; + case XK_KP_0: + case XK_KP_Insert: return SCI_K_INSERT; + case XK_End: + case XK_KP_End: + case XK_KP_1: return SCI_K_END; + case XK_Down: + case XK_KP_Down: + case XK_KP_2: return SCI_K_DOWN; + case XK_Page_Down: + case XK_KP_Page_Down: + case XK_KP_3: return SCI_K_PGDOWN; + case XK_Left: + case XK_KP_Left: + case XK_KP_4: return SCI_K_LEFT; + case XK_KP_Begin: + case XK_KP_5: return SCI_K_CENTER; + case XK_Right: + case XK_KP_Right: + case XK_KP_6: return SCI_K_RIGHT; + case XK_Home: + case XK_KP_Home: + case XK_KP_7: return SCI_K_HOME; + case XK_Up: + case XK_KP_Up: + case XK_KP_8: return SCI_K_UP; + case XK_Page_Up: + case XK_KP_Page_Up: + case XK_KP_9: return SCI_K_PGUP; + + case XK_F1: return SCI_K_F1; + case XK_F2: return SCI_K_F2; + case XK_F3: return SCI_K_F3; + case XK_F4: return SCI_K_F4; + case XK_F5: return SCI_K_F5; + case XK_F6: return SCI_K_F6; + case XK_F7: return SCI_K_F7; + case XK_F8: return SCI_K_F8; + case XK_F9: return SCI_K_F9; + case XK_F10: return SCI_K_F10; + + + case XK_Control_L: + case XK_Control_R:/* S->buckystate |= SCI_EVM_CTRL; return 0; */ + case XK_Alt_L: + case XK_Alt_R:/* S->buckystate |= SCI_EVM_ALT; return 0; */ + case XK_Caps_Lock: + case XK_Shift_Lock:/* S->buckystate ^= SCI_EVM_CAPSLOCK; return 0; */ + case XK_Scroll_Lock:/* S->buckystate ^= SCI_EVM_SCRLOCK; return 0; */ + case XK_Num_Lock:/* S->buckystate ^= SCI_EVM_NUMLOCK; return 0; */ + case XK_Shift_L:/* S->buckystate |= SCI_EVM_LSHIFT; return 0; */ + case XK_Shift_R:/* S->buckystate |= SCI_EVM_RSHIFT; return 0; */ + return 0; + default: + break; + } + + + if (flags & SCI_XLIB_NLS) { + /* Localised key lookup */ + XLookupString(&(key_event->xkey), character, 1, &xkey, NULL); + } + + if ((xkey >= ' ') && (xkey <= '~')) + return xkey; /* All printable ASCII characters */ + + switch (xkey) { + case XK_KP_Add: return '+'; + case XK_KP_Divide: return '/'; + case XK_KP_Subtract: return '-'; + case XK_KP_Multiply: return '*'; + } + + if (*character) + return xkey; /* Should suffice for all practical purposes */ + + sciprintf("Unknown X keysym: %04x\n", xkey); + return 0; +} + + +void +x_get_event(gfx_driver_t *drv, int eventmask, long wait_usec, sci_event_t *sci_event) +{ + int x_button_xlate[] = {0, 1, 3, 2, 4, 5}; + XEvent event; + Window window = S->window; + Display *display = S->display; + struct timeval ctime, timeout_time, sleep_time; + long usecs_to_sleep; + + eventmask |= ExposureMask; /* Always check for this */ + + gettimeofday(&timeout_time, NULL); + timeout_time.tv_usec += wait_usec; + + /* Calculate wait time */ + timeout_time.tv_sec += (timeout_time.tv_usec / 1000000); + timeout_time.tv_usec %= 1000000; + + do { + int hasnext_event = 1; + + while (hasnext_event) { + if (sci_event) { /* Capable of handling any event? */ + hasnext_event = XPending(display); + if (hasnext_event) + XNextEvent(display, &event); + } else + hasnext_event = XCheckWindowEvent(display, window, eventmask, &event); + + + if (hasnext_event) + switch (event.type) { + + case ReparentNotify: + case ConfigureNotify: + case MapNotify: + case UnmapNotify: + break; + + case KeyPress: { + int modifiers = event.xkey.state; + char ch = 0; + sci_event->type = SCI_EVT_KEYBOARD; + + S->buckystate = ((flags & SCI_XLIB_INSERT_MODE)? SCI_EVM_INSERT : 0) + | (((modifiers & LockMask)? SCI_EVM_LSHIFT | SCI_EVM_RSHIFT : 0) + | ((modifiers & ControlMask)? SCI_EVM_CTRL : 0) + | ((modifiers & (Mod1Mask | Mod4Mask))? SCI_EVM_ALT : 0) + | ((modifiers & Mod2Mask)? SCI_EVM_NUMLOCK : 0) + | ((modifiers & Mod5Mask)? SCI_EVM_SCRLOCK : 0)) + ^ ((modifiers & ShiftMask)? SCI_EVM_LSHIFT | SCI_EVM_RSHIFT : 0); + + sci_event->buckybits = S->buckystate; + sci_event->data = + x_map_key(drv, &event, &ch); + + if (ch) + sci_event->character = ch; + else + sci_event->character = sci_event->data; + + if (sci_event->data == SCI_K_INSERT) + flags ^= SCI_XLIB_INSERT_MODE; + + if (sci_event->data) + return; + + break; + } + + case KeyRelease: + /*x_unmap_key(drv, event.xkey.keycode);*/ + break; + + case ButtonPress: { + sci_event->type = SCI_EVT_MOUSE_PRESS; + sci_event->buckybits = S->buckystate; + sci_event->data = x_button_xlate[event.xbutton.button]; + return; + } + + case ButtonRelease: { + sci_event->type = SCI_EVT_MOUSE_RELEASE; + sci_event->buckybits = S->buckystate; + sci_event->data = x_button_xlate[event.xbutton.button]; + return; + } + + case MotionNotify: { + + drv->pointer_x = event.xmotion.x; + drv->pointer_y = event.xmotion.y; + if (!sci_event) + /* Wake up from sleep */ + return; + } + break; + + case GraphicsExpose: + case Expose: { + XCopyArea(S->display, S->visual[0], S->window, S->gc, + event.xexpose.x, event.xexpose.y, event.xexpose.width, event.xexpose.height, + event.xexpose.x, event.xexpose.y); + } + break; + + case NoExpose: + break; + + default: + ERROR("Received unhandled X event %04x\n", event.type); + } + } + + gettimeofday(&ctime, NULL); + + usecs_to_sleep = (timeout_time.tv_sec > ctime.tv_sec)? 1000000 : 0; + usecs_to_sleep += timeout_time.tv_usec - ctime.tv_usec; + if (ctime.tv_sec > timeout_time.tv_sec) usecs_to_sleep = -1; + + + if (usecs_to_sleep > 0) { + + if (usecs_to_sleep > 10000) + usecs_to_sleep = 10000; /* Sleep for a maximum of 10 ms */ + + sleep_time.tv_usec = usecs_to_sleep; + sleep_time.tv_sec = 0; + + select(0, NULL, NULL, NULL, &sleep_time); /* Sleep. */ + } + + } while (usecs_to_sleep >= 0); + + if (sci_event) + sci_event->type = SCI_EVT_NONE; /* No event. */ +} + + +static sci_event_t +xlib_get_event(struct _gfx_driver *drv) +{ + sci_event_t input; + + x_get_event(drv, PointerMotionMask | StructureNotifyMask | ButtonPressMask + | ButtonReleaseMask | KeyPressMask | KeyReleaseMask, + 0, &input); + + return input; +} + + +static int +xlib_usec_sleep(struct _gfx_driver *drv, long usecs) +{ + x_get_event(drv, PointerMotionMask | StructureNotifyMask, usecs, NULL); + return GFX_OK; +} + +gfx_driver_t +gfx_driver_xlib = { + "xlib", + "0.6a", + SCI_GFX_DRIVER_MAGIC, + SCI_GFX_DRIVER_VERSION, + NULL, + 0, 0, + GFX_CAPABILITY_STIPPLED_LINES | GFX_CAPABILITY_MOUSE_SUPPORT + | GFX_CAPABILITY_MOUSE_POINTER | GFX_CAPABILITY_PIXMAP_REGISTRY + | GFX_CAPABILITY_FINE_LINES | GFX_CAPABILITY_WINDOWED + | GFX_CAPABILITY_KEYTRANSLATE, + 0/*GFX_DEBUG_POINTER | GFX_DEBUG_UPDATES | GFX_DEBUG_PIXMAPS | GFX_DEBUG_BASIC*/, + xlib_set_parameter, + xlib_init_specific, + xlib_init, + xlib_exit, + xlib_draw_line, +#ifdef HAVE_RENDER + xlib_draw_filled_rect_RENDER, +#else + xlib_draw_filled_rect, +#endif + xlib_register_pixmap, + xlib_unregister_pixmap, + xlib_draw_pixmap, + xlib_grab_pixmap, + xlib_update, + xlib_set_static_buffer, + xlib_set_pointer, + xlib_set_palette, + xlib_get_event, + xlib_usec_sleep, + NULL +}; + +#endif /* X_DISPLAY_MISSING */ |