/* ScummVM - Graphic Adventure Engine * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT * file distributed with this source distribution. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * $URL$ * $Id$ * */ #include "common/scummsys.h" #include "common/system.h" #include "common/events.h" #include "sci/gfx/gfx_driver.h" #include "sci/gfx/gfx_tools.h" namespace Sci { 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, Common::Point start, Common::Point 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) { Common::Point nstart, nend; nstart.x = CLIP(start.x, 0, xsize); nstart.y = CLIP(start.y, 0, ysize); nend.x = CLIP(end.x, 0, xsize - 1); nend.y = CLIP(end.y, 0, 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, Common::Point 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) { if (index < 0 || index > 255) { GFXERROR("Attempt to set invalid palette entry %d\n", index); return GFX_ERROR; } 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 = { SCI_EVT_NONE, 0, 0, 0 }; 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 = SCI_K_F1 + ((input.data - Common::KEYCODE_F1)<<8); if (input.buckybits & (SCI_EVM_LSHIFT | SCI_EVM_RSHIFT)) input.character = input.data + SCI_K_SHIFT_F1 - SCI_K_F1; 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; default: 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 = { NULL, 0, 0, 0, // flags here 0, NULL, scummvm_init_specific, scummvm_init, scummvm_exit, scummvm_draw_line, scummvm_draw_filled_rect, 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 }; } // End of namespace Sci