diff options
Diffstat (limited to 'backends/platform/x11')
-rw-r--r-- | backends/platform/x11/build.rules | 7 | ||||
-rw-r--r-- | backends/platform/x11/module.mk | 10 | ||||
-rw-r--r-- | backends/platform/x11/x11.cpp | 1037 | ||||
-rw-r--r-- | backends/platform/x11/x11.h | 195 |
4 files changed, 1249 insertions, 0 deletions
diff --git a/backends/platform/x11/build.rules b/backends/platform/x11/build.rules new file mode 100644 index 0000000000..1540d452d6 --- /dev/null +++ b/backends/platform/x11/build.rules @@ -0,0 +1,7 @@ +# Build settings for the X11 backend +MODULES += backends/x11 +OBJS += backends/x11/x11.o +DEFINES += -DUNIX -DX11_BACKEND +LDFLAGS += -L/usr/X11R6/lib -L/usr/local/lib +INCLUDES+= -I/usr/X11R6/include +LIBS += -lpthread -lXext -lX11 diff --git a/backends/platform/x11/module.mk b/backends/platform/x11/module.mk new file mode 100644 index 0000000000..22015b53be --- /dev/null +++ b/backends/platform/x11/module.mk @@ -0,0 +1,10 @@ +MODULE := backends/platform/x11 + +MODULE_OBJS := \ + x11.o + +MODULE_DIRS += \ + backends/platform/x11/ + +# We don't use the rules.mk here on purpose +OBJS := $(addprefix $(MODULE)/, $(MODULE_OBJS)) $(OBJS) diff --git a/backends/platform/x11/x11.cpp b/backends/platform/x11/x11.cpp new file mode 100644 index 0000000000..ce020c7a8e --- /dev/null +++ b/backends/platform/x11/x11.cpp @@ -0,0 +1,1037 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2001 Ludvig Strigeus + * Copyright (C) 2001-2006 The ScummVM project + * + * 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$ + * + */ + +/* The bare pure X11 port done by Lionel 'BBrox' Ulmer */ + +#include "common/stdafx.h" +#include "common/scummsys.h" +#include "common/system.h" +#include "common/util.h" +#include "base/main.h" +#include "backends/intern.h" +#include "backends/platform/x11/x11.h" + +#include <stdio.h> +#include <assert.h> + +#include <sys/time.h> +#include <unistd.h> + +#include <sys/ipc.h> +#include <sys/shm.h> + +#include <X11/Xlib.h> +#include <X11/Xutil.h> +#include <X11/extensions/XShm.h> + +#ifdef __linux__ +#include <linux/soundcard.h> +#else +#include <sys/soundcard.h> +#endif + +#include <sched.h> +#include <pthread.h> + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <sys/ioctl.h> + +int main(int argc, char *argv[]) { + g_system = OSystem_X11::create(0, 0); + assert(g_system); + + // Invoke the actual ScummVM main entry point: + int res = scummvm_main(argc, argv); + g_system->quit(); // TODO: Consider removing / replacing this! + return res; +} + +OSystem *OSystem_X11::create(int gfx_mode, bool full_screen) { + OSystem_X11 *syst = new OSystem_X11(); + return syst; +} + +OSystem_X11::OSystem_X11() { + /* Some members initialization */ + _fake_right_mouse = 0; + _report_presses = 1; + _current_shake_pos = 0; + _new_shake_pos = 0; + _palette_changed = false; + _num_of_dirty_rects = 0; + _overlay_visible = false; + _mouse_state_changed = true; + _mouse_visible = true; + _ms_buf = NULL; + _curMouseState.x = 0; + _curMouseState.y = 0; + _curMouseState.hot_x = 0; + _curMouseState.hot_y = 0; + _curMouseState.w = 0; + _curMouseState.h = 0; + _palette16 = 0; + _palette32 = 0; + _bytesPerPixel = 0; + _image = 0; + _local_fb = 0; + _local_fb_overlay = 0; +} + +OSystem_X11::~OSystem_X11() { + XFree(_image); + if (_palette16) + free(_palette16); + + if (_palette32) + free(_palette32); + + if (_ms_buf) + free(_ms_buf); + + free(_local_fb_overlay); + free(_local_fb); +} + +void OSystem_X11::initBackend() { + char buf[512]; + XWMHints *wm_hints; + XGCValues values; + XTextProperty window_name; + char *name = (char *)&buf; + /* For the_window title */ + sprintf(buf, "ScummVM"); + + _display = XOpenDisplay(NULL); + if (_display == NULL) { + error("Could not open display !\n"); + exit(1); + } + + if (XShmQueryExtension(_display)!=True) + error("No Shared Memory Extension present"); + + _screen = DefaultScreen(_display); + _depth = DefaultDepth(_display, _screen); + switch (_depth) { + case 16 : + _bytesPerPixel = 2; + break; + case 24 : + case 32 : + _bytesPerPixel = 4; + break; + } + + if (!_bytesPerPixel) + error("Your screen depth is %ibit. Values other than 16, 24 and 32bit are currently not supported", _depth); + + _window_width = 320; + _window_height = 200; + _scumm_x = 0; + _scumm_y = 0; + _window = XCreateSimpleWindow(_display, XRootWindow(_display, _screen), 0, 0, 320, 200, 0, 0, 0); + wm_hints = XAllocWMHints(); + if (wm_hints == NULL) { + error("Not enough memory to allocate Hints !\n"); + exit(1); + } + wm_hints->flags = InputHint | StateHint; + wm_hints->input = True; + wm_hints->initial_state = NormalState; + XStringListToTextProperty(&name, 1, &window_name); + XSetWMProperties(_display, _window, &window_name, &window_name, + NULL /* argv */ , 0 /* argc */ , NULL /* size hints */ , + wm_hints, NULL /* class hints */ ); + XFree(wm_hints); + + XSelectInput(_display, _window, + ExposureMask | KeyPressMask | KeyReleaseMask | + PointerMotionMask | ButtonPressMask | ButtonReleaseMask | StructureNotifyMask); + + values.foreground = BlackPixel(_display, _screen); + _black_gc = XCreateGC(_display, _window, GCForeground, &values); + + XMapWindow(_display, _window); + XFlush(_display); + + _fb_width = 0; + _fb_height = 0; + + if (!_palette16) + _palette16 = (uint16 *)calloc(256, sizeof(uint16)); + if (!_palette32 && _bytesPerPixel == 4) + _palette32 = (uint32 *)calloc(256, sizeof(uint32)); + + while (1) { + XEvent event; + XNextEvent(_display, &event); + switch (event.type) { + case Expose: + goto out_of_loop; + } + } +out_of_loop: + create_empty_cursor(); + + /* Initialize the timer routines */ + _timer_active = false; + + /* And finally start the local timer */ + gettimeofday(&_start_time, NULL); + +} + +#undef CAPTURE_SOUND +#define FRAG_SIZE 4096 + +static void *sound_and_music_thread(void *params) { + /* Init sound */ + int sound_fd, param, frag_size; + uint8 sound_buffer[FRAG_SIZE]; + OSystem::SoundProc sound_proc = ((THREAD_PARAM *)params)->sound_proc; + void *proc_param = ((THREAD_PARAM *)params)->param; + +#ifdef CAPTURE_SOUND + FILE *f = fopen("sound.raw", "wb"); +#endif + + sound_fd = open("/dev/dsp", O_WRONLY); + audio_buf_info info; + if (sound_fd < 0) { + warning("Error opening sound device!\n"); + return NULL; + } + param = 0; + frag_size = FRAG_SIZE /* audio fragment size */ ; + while (frag_size) { + frag_size >>= 1; + param++; + } + param--; + param |= /* audio_fragment_num */ 3 << 16; + if (ioctl(sound_fd, SNDCTL_DSP_SETFRAGMENT, ¶m) != 0) { + warning("Error in the SNDCTL_DSP_SETFRAGMENT ioctl!\n"); + return NULL; + } + param = AFMT_S16_LE; + if (ioctl(sound_fd, SNDCTL_DSP_SETFMT, ¶m) == -1) { + warning("Error in the SNDCTL_DSP_SETFMT ioctl!\n"); + return NULL;; + } + if (param != AFMT_S16_LE) { + warning("AFMT_S16_LE not supported!\n"); + return NULL; + } + param = 2; + if (ioctl(sound_fd, SNDCTL_DSP_CHANNELS, ¶m) == -1) { + warning("Error in the SNDCTL_DSP_CHANNELS ioctl!\n"); + return NULL; + } + if (param != 2) { + warning("Stereo mode not supported!\n"); + return NULL; + } + param = SAMPLES_PER_SEC; + if (ioctl(sound_fd, SNDCTL_DSP_SPEED, ¶m) == -1) { + warning("Error in the SNDCTL_DSP_SPEED ioctl!\n"); + return NULL; + } + if (param != SAMPLES_PER_SEC) { + warning("%d kHz not supported!\n", SAMPLES_PER_SEC); + return NULL; + } + if (ioctl(sound_fd, SNDCTL_DSP_GETOSPACE, &info) != 0) { + warning("SNDCTL_DSP_GETOSPACE"); + return NULL; + } + + sched_yield(); + while (1) { + uint8 *buf = (uint8 *)sound_buffer; + int size, written; + + sound_proc(proc_param, (byte *)sound_buffer, FRAG_SIZE); +#ifdef CAPTURE_SOUND + fwrite(buf, 2, FRAG_SIZE >> 1, f); + fflush(f); +#endif + size = FRAG_SIZE; + while (size > 0) { + written = write(sound_fd, buf, size); + buf += written; + size -= written; + } + } + + return NULL; +} + +/* Function used to hide the mouse cursor */ +void OSystem_X11::create_empty_cursor() { + XColor bg; + Pixmap pixmapBits; + Cursor cursor = None; + static const char data[] = { 0 }; + + bg.red = bg.green = bg.blue = 0x0000; + pixmapBits = XCreateBitmapFromData(_display, XRootWindow(_display, _screen), data, 1, 1); + if (pixmapBits) { + cursor = XCreatePixmapCursor(_display, pixmapBits, pixmapBits, &bg, &bg, 0, 0); + XFreePixmap(_display, pixmapBits); + } + XDefineCursor(_display, _window, cursor); +} + +bool OSystem_X11::hasFeature(Feature f) { + return false; +} + +void OSystem_X11::setFeatureState(Feature f, bool enable) { +} + +bool OSystem_X11::getFeatureState(Feature f) { + return false; +} + +const OSystem::GraphicsMode *OSystem_X11::getSupportedGraphicsModes() const { + static const OSystem::GraphicsMode mode = {"1x", "Normal mode", 0}; + return &mode; +} + +int OSystem_X11::getDefaultGraphicsMode() const { + return 0; +} + +bool OSystem_X11::setGraphicsMode(int mode) { + return (mode == 0); +} + +int OSystem_X11::getGraphicsMode() const { + return 0; +} + + +uint32 OSystem_X11::getMillis() { + struct timeval current_time; + gettimeofday(¤t_time, NULL); + return (uint32)(((current_time.tv_sec - _start_time.tv_sec) * 1000) + + ((current_time.tv_usec - _start_time.tv_usec) / 1000)); +} + +void OSystem_X11::initSize(uint w, uint h) { + //debug("initSize(%i, %i)", w, h); + static XShmSegmentInfo shminfo; + + if (((uint)_fb_width != w) || ((uint)_fb_height != w)) { + _fb_width = w; + _fb_height = h; + + /* We need to change the size of the X11_window */ + XWindowChanges new_values; + + new_values.width = _fb_width; + new_values.height = _fb_height; + + XConfigureWindow(_display,_window, CWWidth | CWHeight, &new_values); + + if (_image) + XFree(_image); + _image = XShmCreateImage(_display, DefaultVisual(_display, _screen), _depth, ZPixmap, NULL, &shminfo,_fb_width,_fb_height); + if (!_image) + error("Couldn't get image by XShmCreateImage()"); + + shminfo.shmid = shmget(IPC_PRIVATE, _image->bytes_per_line * _image->height, IPC_CREAT | 0700); + if (shminfo.shmid < 0) + error("Couldn't allocate image data by shmget()"); + + _image->data = shminfo.shmaddr = (char *)shmat(shminfo.shmid, 0, 0); + shminfo.readOnly = False; + if (XShmAttach(_display, &shminfo) == 0) { + error("Could not attach shared memory segment !\n"); + exit(1); + } + shmctl(shminfo.shmid, IPC_RMID, 0); + + if (_local_fb) + free(_local_fb); + if (_local_fb_overlay) + free(_local_fb_overlay); + /* Initialize the 'local' frame buffer and the palette */ + _local_fb = (uint8 *)calloc(_fb_width * _fb_height, sizeof(uint8)); + _local_fb_overlay = (uint16 *)calloc(_fb_width * _fb_height, sizeof(uint16)); + + } +} + +bool OSystem_X11::setSoundCallback(SoundProc proc, void *param) { + static THREAD_PARAM thread_param; + + /* And finally start the music thread */ + thread_param.param = param; + thread_param.sound_proc = proc; + + pthread_create(&_sound_thread, NULL, sound_and_music_thread, (void *)&thread_param); + + return true; +} + +void OSystem_X11::clearSoundCallback() { + // TODO implement this... + // The sound_thread has to be stopped in a nice way. In particular, + // using pthread_kill would be a bad idea. Rather, use pthread_cancel, + // or maybe a global variable, to achieve this. + // This method shouldn't return until the sound thread really has stopped. +} + + +void OSystem_X11::setPalette(const byte *colors, uint start, uint num) { + uint16 *pal = &(_palette16[start]); + const byte *data = colors; + + if (_bytesPerPixel == 4) { + for (uint i = start; i < start+num; i++) { + //_palette32[i] = ((uint32 *)colors)[i]; + _palette32[i] = (colors[i * 4 + 0] << 16) | (colors[i * 4 + 1] << 8) | (colors[i * 4 + 2] << 0); + } + } + + do { + *pal++ = ((data[0] & 0xF8) << 8) | ((data[1] & 0xFC) << 3) | (data[2] >> 3); + data += 4; + num--; + } while (num > 0); + + _palette_changed = true; +} + +#define AddDirtyRec(xi,yi,wi,hi) \ + if (_num_of_dirty_rects < MAX_NUMBER_OF_DIRTY_RECTS) { \ + _ds[_num_of_dirty_rects].x = xi; \ + _ds[_num_of_dirty_rects].y = yi; \ + _ds[_num_of_dirty_rects].w = wi; \ + _ds[_num_of_dirty_rects].h = hi; \ + _num_of_dirty_rects++; \ + } + +void OSystem_X11::copyRectToScreen(const byte *buf, int pitch, int x, int y, int w, int h) { + uint8 *dst; + + if (y < 0) { + h += y; + buf -= y * pitch; + y = 0; + } + if (h > (_fb_height - y)) { + h = _fb_height - y; + } + + dst = _local_fb + _fb_width * y + x; + + if (h <= 0) + return; + + AddDirtyRec(x, y, w, h); + while (h-- > 0) { + memcpy(dst, buf, w); + dst +=_fb_width; + buf += pitch; + } +} + +void OSystem_X11::blit(const DirtyRect *d, uint16 *dst, int pitch) { + uint8 *ptr_src = _local_fb + (_fb_width * d->y) + d->x; + uint16 *ptr_dst = dst + ((_fb_width * d->y) + d->x); + int x, y; + + for (y = 0; y < d->h; y++) { + for (x = 0; x < d->w; x++) { + *ptr_dst++ = _palette16[*ptr_src++]; + } + ptr_dst += pitch - d->w; + ptr_src +=_fb_width - d->w; + } +} + +void OSystem_X11::blit_convert(const DirtyRect *d, uint8 *dst, int pitch) { + uint8 *ptr_src = _local_fb + (_fb_width * d->y) + d->x; + uint8 *ptr_dst = dst + ((_fb_width * d->y) + d->x) * _bytesPerPixel; + int x, y; + + switch (_bytesPerPixel) { + case 2: + for (y = 0; y < d->h; y++) { + for (x = 0; x < d->w; x++) { + *ptr_dst = _palette16[*ptr_src++]; + ptr_dst += _bytesPerPixel; + } + ptr_dst += (pitch - d->w) * _bytesPerPixel; + ptr_src +=_fb_width - d->w; + } + break; + case 4: + for (y = 0; y < d->h; y++) { + for (x = 0; x < d->w; x++) { + *(uint32 *)ptr_dst = _palette32[*ptr_src]; + ptr_dst += _bytesPerPixel; + ptr_src++; + } + ptr_dst += (pitch - d->w) * _bytesPerPixel; + ptr_src += _fb_width - d->w; + } + } +} + +void OSystem_X11::updateScreen_helper(const DirtyRect *d, DirtyRect *dout) { + + if (_overlay_visible == false) { + blit_convert(d, (uint8 *)_image->data, _fb_width); + } else { + uint16 *ptr_src = _local_fb_overlay + (_fb_width * d->y) + d->x; + uint8 *ptr_dst = (uint8 *)_image->data + ((_fb_width * d->y) + d->x) * _bytesPerPixel; + + int y; + + switch (_bytesPerPixel) { + case 2: + for (y = 0; y < d->h; y++) { + memcpy(ptr_dst, ptr_src, d->w * sizeof(uint16)); + ptr_dst += _fb_width * sizeof(uint16); + ptr_src += _fb_width; + } + break; + case 4: + uint16 currLine, x; + register uint16 currPixel; + for (y = d->y; y < d->y + d->h; y++) { + currLine = y * _fb_width; + for (x = d->x; x < d->x + d->w; x++) { + currPixel = _local_fb_overlay[(currLine + x)]; + *(uint32 *)ptr_dst = ((currPixel & 0xF800) << 8) + ((currPixel & 0x07E0) << 5) + + ((currPixel & 0x001F) << 3); + ptr_dst += sizeof(uint32); + } + ptr_dst += (_fb_width - d->w) * _bytesPerPixel; + } + + } + } + if (d->x < dout->x) + dout->x = d->x; + if (d->y < dout->y) + dout->y = d->y; + if ((d->x + d->w) > dout->w) + dout->w = d->x + d->w; + if ((d->y + d->h) > dout->h) + dout->h = d->y + d->h; +} + +void OSystem_X11::updateScreen() { + bool full_redraw = false; + bool need_redraw = false; + static const DirtyRect ds_full = { 0, 0, _fb_width, _fb_height }; + DirtyRect dout = {_fb_width, _fb_height, 0, 0 }; + + if (_palette_changed) { + full_redraw = true; + _num_of_dirty_rects = 0; + _palette_changed = false; + } else if (_num_of_dirty_rects >= MAX_NUMBER_OF_DIRTY_RECTS) { + full_redraw = true; + _num_of_dirty_rects = 0; + } + + if (full_redraw) { + updateScreen_helper(&ds_full, &dout); + need_redraw = true; + } else if ((_num_of_dirty_rects > 0) || (_mouse_state_changed == true)) { + need_redraw = true; + while (_num_of_dirty_rects > 0) { + _num_of_dirty_rects--; + updateScreen_helper(&(_ds[_num_of_dirty_rects]), &dout); + } + } + + /* Then 'overlay' the mouse on the image */ + draw_mouse(&dout); + + if (_current_shake_pos != _new_shake_pos) { + /* Redraw first the 'black borders' in case of resize */ + if (_current_shake_pos < _new_shake_pos) + XFillRectangle(_display,_window, _black_gc, 0, _current_shake_pos, _window_width, _new_shake_pos); + else + XFillRectangle(_display,_window, _black_gc, 0, _window_height - _current_shake_pos, + _window_width,_window_height - _new_shake_pos); + XShmPutImage(_display, _window, DefaultGC(_display, _screen), _image, + 0, 0, _scumm_x, _scumm_y + _new_shake_pos, _fb_width, _fb_height, 0); + _current_shake_pos = _new_shake_pos; + } else if (need_redraw == true) { + XShmPutImage(_display, _window, DefaultGC(_display, _screen), _image, + dout.x, dout.y, _scumm_x + dout.x, _scumm_y + dout.y + _current_shake_pos, + dout.w - dout.x, dout.h - dout.y, 0); + XFlush(_display); + } +} + +bool OSystem_X11::showMouse(bool visible) +{ + if (_mouse_visible == visible) + return visible; + + bool last = _mouse_visible; + _mouse_visible = visible; + + if ((visible == false) && (_mouse_state_changed == false)) { + undraw_mouse(); + } + _mouse_state_changed = true; + + return last; +} + +void OSystem_X11::quit() { + exit(0); +} + +void OSystem_X11::setWindowCaption(const char *caption) { + //debug("setWindowCaption('%s')", caption); +} + +void OSystem_X11::undraw_mouse() { + AddDirtyRec(_oldMouseState.x, _oldMouseState.y, _oldMouseState.w, _oldMouseState.h); +} + +void OSystem_X11::draw_mouse(DirtyRect *dout) { + //debug("draw_mouse()"); + _mouse_state_changed = false; + + if (_mouse_visible == false) + return; + + int xdraw = _curMouseState.x - _curMouseState.hot_x; + int ydraw = _curMouseState.y - _curMouseState.hot_y; + int w = _curMouseState.w; + int h = _curMouseState.h; + int real_w; + int real_h; + + uint8 *dst; + const byte *buf = _ms_buf; + + if (ydraw < 0) { + real_h = h + ydraw; + buf += (-ydraw) * w; + ydraw = 0; + } else { + real_h = (ydraw + h) > _fb_height ? (_fb_height - ydraw) : h; + } + if (xdraw < 0) { + real_w = w + xdraw; + buf += (-xdraw); + xdraw = 0; + } else { + real_w = (xdraw + w) > _fb_width ? (_fb_width - xdraw) : w; + } + + dst = (uint8 *)_image->data + ((ydraw *_fb_width) + xdraw) * _bytesPerPixel; + + if ((real_h == 0) || (real_w == 0)) { + return; + } + + + if (xdraw < dout->x) + dout->x = xdraw; + if (ydraw < dout->y) + dout->y = ydraw; + if ((xdraw + real_w) > dout->w) + dout->w = xdraw + real_w; + if ((ydraw + real_h) > dout->h) + dout->h = ydraw + real_h; + + _oldMouseState.x = xdraw; + _oldMouseState.y = ydraw; + _oldMouseState.w = real_w; + _oldMouseState.h = real_h; + + while (real_h > 0) { + int width = real_w; + while (width > 0) { + byte color = *buf; + if (color != _mouseKeycolor) { + if (_depth == 16) + *(uint16 *)dst = _palette16[color]; + else { + *(uint32 *)dst = _palette32[color]; + } + } + buf++; + dst += _bytesPerPixel; + width--; + } + buf += w - real_w; + dst += (_fb_width - real_w) * _bytesPerPixel; + real_h--; + } +} + +void OSystem_X11::set_mouse_pos(int x, int y) { + if ((x != _curMouseState.x) || (y != _curMouseState.y)) { + _curMouseState.x = x; + _curMouseState.y = y; + if (_mouse_state_changed == false) { + undraw_mouse(); + } + _mouse_state_changed = true; + } +} + +void OSystem_X11::warpMouse(int x, int y) { + set_mouse_pos(x, y); +} + +void OSystem_X11::setMouseCursor(const byte *buf, uint w, uint h, int hotspot_x, int hotspot_y, byte keycolor, int cursorTargetScale) { + _curMouseState.w = w; + _curMouseState.h = h; + _curMouseState.hot_x = hotspot_x; + _curMouseState.hot_y = hotspot_y; + + if (_ms_buf) + free(_ms_buf); + _ms_buf = (byte *) malloc(w * h); + memcpy(_ms_buf, buf, w * h); + + if (_mouse_state_changed == false) { + undraw_mouse(); + } + _mouseKeycolor = keycolor; + _mouse_state_changed = true; +} + +void OSystem_X11::setShakePos(int shake_pos) { + if (_new_shake_pos != shake_pos) { + if (_mouse_state_changed == false) { + undraw_mouse(); + } + _mouse_state_changed = true; + } + _new_shake_pos = shake_pos; +} + +int OSystem_X11::getOutputSampleRate() const { + return SAMPLES_PER_SEC; +} + +void OSystem_X11::delayMillis(uint msecs) { + usleep(msecs * 1000); +} + +bool OSystem_X11::pollEvent(Event &scumm_event) { + /* First, handle timers */ + uint32 current_msecs = getMillis(); + + if (_timer_active && (current_msecs >= _timer_next_expiry)) { + _timer_duration = _timer_callback(_timer_duration); + _timer_next_expiry = current_msecs + _timer_duration; + } + + while (XPending(_display)) { + XEvent event; + + XNextEvent(_display, &event); + switch (event.type) { + case Expose:{ + int real_w, real_h; + int real_x, real_y; + real_x = event.xexpose.x; + real_y = event.xexpose.y; + real_w = event.xexpose.width; + real_h = event.xexpose.height; + if (real_x < _scumm_x) { + real_w -= _scumm_x - real_x; + real_x = 0; + } else { + real_x -= _scumm_x; + } + if (real_y < _scumm_y) { + real_h -= _scumm_y - real_y; + real_y = 0; + } else { + real_y -= _scumm_y; + } + if ((real_h <= 0) || (real_w <= 0)) + break; + if ((real_x >=_fb_width) || (real_y >=_fb_height)) + break; + + if ((real_x + real_w) >=_fb_width) { + real_w =_fb_width - real_x; + } + if ((real_y + real_h) >=_fb_height) { + real_h =_fb_height - real_y; + } + + /* Compute the intersection of the expose event with the real ScummVM display zone */ + AddDirtyRec(real_x, real_y, real_w, real_h); + } + break; + + case KeyPress:{ + /* I am using keycodes here and NOT keysyms to be sure that even if the user + remaps his iPAQ's keyboard, it will still work. + */ + int keycode = -1; + int ascii = -1; + byte mode = 0; + + if (event.xkey.state & 0x01) + mode |= KBD_SHIFT; + if (event.xkey.state & 0x04) + mode |= KBD_CTRL; + if (event.xkey.state & 0x08) + mode |= KBD_ALT; + switch (event.xkey.keycode) { + + case 9: /* Escape on my PC */ + case 130: /* Calendar on the iPAQ */ + keycode = 27; + break; + + case 71: /* F5 on my PC */ + case 128: /* Record on the iPAQ */ + keycode = 319; + break; + + case 65: /* Space on my PC */ + case 131: /* Schedule on the iPAQ */ + keycode = 32; + break; + + case 132: + _report_presses = 0; + break; + + case 133: + _fake_right_mouse = 1; + break; + + default:{ + KeySym xsym; + xsym = XKeycodeToKeysym(_display, event.xkey.keycode, 0); + keycode = xsym; + if ((xsym >= 'a') && (xsym <= 'z') && (event.xkey.state & 0x01)) + xsym &= ~0x20; /* Handle shifted keys */ + ascii = xsym; + } + } + if (keycode != -1) { + scumm_event.type = EVENT_KEYDOWN; + scumm_event.kbd.keycode = keycode; + scumm_event.kbd.ascii = (ascii != -1 ? ascii : keycode); + scumm_event.kbd.flags = mode; + return true; + } + } + break; + + case KeyRelease:{ + /* I am using keycodes here and NOT keysyms to be sure that even if the user + remaps his iPAQ's keyboard, it will still work. + */ + int keycode = -1; + int ascii = -1; + byte mode = 0; + + if (event.xkey.state & 0x01) + mode |= KBD_SHIFT; + if (event.xkey.state & 0x04) + mode |= KBD_CTRL; + if (event.xkey.state & 0x08) + mode |= KBD_ALT; + switch (event.xkey.keycode) { + case 132: /* 'Q' on the iPAQ */ + _report_presses = 1; + break; + + case 133: /* Arrow on the iPAQ */ + _fake_right_mouse = 0; + break; + + default:{ + KeySym xsym; + xsym = XKeycodeToKeysym(_display, event.xkey.keycode, 0); + keycode = xsym; + if ((xsym >= 'a') && (xsym <= 'z') && (event.xkey.state & 0x01)) + xsym &= ~0x20; /* Handle shifted keys */ + ascii = xsym; + } + } + if (keycode != -1) { + scumm_event.type = EVENT_KEYUP; + scumm_event.kbd.keycode = keycode; + scumm_event.kbd.ascii = (ascii != -1 ? ascii : keycode); + scumm_event.kbd.flags = mode; + return true; + } + } + break; + + case ButtonPress: + if (_report_presses != 0) { + if (event.xbutton.button == 1) { + if (_fake_right_mouse == 0) { + scumm_event.type = EVENT_LBUTTONDOWN; + } else { + scumm_event.type = EVENT_RBUTTONDOWN; + } + } else if (event.xbutton.button == 3) + scumm_event.type = EVENT_RBUTTONDOWN; + scumm_event.mouse.x = event.xbutton.x - _scumm_x; + scumm_event.mouse.y = event.xbutton.y - _scumm_y; + return true; + } + break; + + case ButtonRelease: + if (_report_presses != 0) { + if (event.xbutton.button == 1) { + if (_fake_right_mouse == 0) { + scumm_event.type = EVENT_LBUTTONUP; + } else { + scumm_event.type = EVENT_RBUTTONUP; + } + } else if (event.xbutton.button == 3) + scumm_event.type = EVENT_RBUTTONUP; + scumm_event.mouse.x = event.xbutton.x - _scumm_x; + scumm_event.mouse.y = event.xbutton.y - _scumm_y; + return true; + } + break; + + case MotionNotify: + scumm_event.type = EVENT_MOUSEMOVE; + scumm_event.mouse.x = event.xmotion.x - _scumm_x; + scumm_event.mouse.y = event.xmotion.y - _scumm_y; + set_mouse_pos(scumm_event.mouse.x, scumm_event.mouse.y); + return true; + + case ConfigureNotify:{ + if ((_window_width != event.xconfigure.width) || (_window_height != event.xconfigure.height)) { + _window_width = event.xconfigure.width; + _window_height = event.xconfigure.height; + _scumm_x = (_window_width -_fb_width) / 2; + _scumm_y = (_window_height -_fb_height) / 2; + XFillRectangle(_display, _window, _black_gc, 0, 0, _window_width, _window_height); + } + } + break; + + default: + printf("Unhandled event : %d\n", event.type); + break; + } + } + + return false; +} + +void OSystem_X11::setTimerCallback(TimerProc callback, int interval) { + if (callback != NULL) { + _timer_duration = interval; + _timer_next_expiry = getMillis() + interval; + _timer_callback = callback; + _timer_active = true; + } else { + _timer_active = false; + } +} + +OSystem::MutexRef OSystem_X11::createMutex(void) { + pthread_mutex_t *mutex = (pthread_mutex_t *) malloc(sizeof(pthread_mutex_t)); + pthread_mutex_init(mutex, NULL); + return (MutexRef)mutex; +} + +void OSystem_X11::lockMutex(MutexRef mutex) { + pthread_mutex_lock((pthread_mutex_t *) mutex); +} + +void OSystem_X11::unlockMutex(MutexRef mutex) { + pthread_mutex_unlock((pthread_mutex_t *) mutex); +} + +void OSystem_X11::deleteMutex(MutexRef mutex) { + pthread_mutex_destroy((pthread_mutex_t *) mutex); + free(mutex); +} + +void OSystem_X11::showOverlay() { + _overlay_visible = true; +} + +void OSystem_X11::hideOverlay() { + _overlay_visible = false; + _palette_changed = true; // This is to force a full redraw to hide the overlay +} + +void OSystem_X11::clearOverlay() { + if (_overlay_visible == false) + return; + DirtyRect d = { 0, 0, _fb_width, _fb_height }; + AddDirtyRec(0, 0, _fb_width, _fb_height); + blit(&d, _local_fb_overlay, _fb_width); +} + +void OSystem_X11::grabOverlay(int16 *dest, int pitch) { + if (_overlay_visible == false) + return; + + DirtyRect d = { 0, 0, _fb_width, _fb_height }; + blit(&d, (uint16 *)dest, pitch); +} + +void OSystem_X11::copyRectToOverlay(const int16 *src, int pitch, int x, int y, int w, int h) { + if (_overlay_visible == false) + return; + uint16 *dst = _local_fb_overlay + x + (y * _fb_width); + AddDirtyRec(x, y, w, h); + while (h > 0) { + memcpy(dst, src, w * sizeof(*dst)); + dst +=_fb_width; + src += pitch; + h--; + } +} + +int16 OSystem_X11::getHeight() { + return _fb_height; +} + +int16 OSystem_X11::getWidth() { + return _fb_width; +} + +void OSystem_X11::grabPalette(byte *colors, uint start, uint num) { + warning("Dummy: grabPalette()"); +} diff --git a/backends/platform/x11/x11.h b/backends/platform/x11/x11.h new file mode 100644 index 0000000000..ba29bc7eab --- /dev/null +++ b/backends/platform/x11/x11.h @@ -0,0 +1,195 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2001 Ludvig Strigeus + * Copyright (C) 2001-2006 The ScummVM project + * + * 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$ + * + */ + +class OSystem_X11:public OSystem { +public: + OSystem_X11(); + ~OSystem_X11(); + + void initBackend(); + + // Determine whether the backend supports the specified feature. + bool hasFeature(Feature f); + + // En-/disable the specified feature. + void setFeatureState(Feature f, bool enable); + + // Query the state of the specified feature. + bool getFeatureState(Feature f); + + // Retrieve a list of all graphics modes supported by this backend. + const GraphicsMode *getSupportedGraphicsModes() const; + + // Return the ID of the 'default' graphics mode. + int getDefaultGraphicsMode() const; + + // Switch to the specified graphics mode. + bool setGraphicsMode(int mode); + + // Determine which graphics mode is currently active. + int getGraphicsMode() const; + + // Set colors of the palette + void setPalette(const byte *colors, uint start, uint num); + + // Set the size of the video bitmap. + // Typically, 320x200 + void initSize(uint w, uint h); + + // Draw a bitmap to screen. + // The screen will not be updated to reflect the new bitmap + void copyRectToScreen(const byte *buf, int pitch, int x, int y, int w, int h); + + // Update the dirty areas of the screen + void updateScreen(); + + // Either show or hide the mouse cursor + bool showMouse(bool visible); + + // Set the position of the mouse cursor + void set_mouse_pos(int x, int y); + + // Warp the mouse cursor. Where set_mouse_pos() only informs the + // backend of the mouse cursor's current position, this function + // actually moves the cursor to the specified position. + void warpMouse(int x, int y); + + // Set the bitmap that's used when drawing the cursor. + void setMouseCursor(const byte *buf, uint w, uint h, int hotspotX, int hotspotY, byte keycolor = 255, int cursorTargetScale = 1); + + // Shaking is used in SCUMM. Set current shake position. + void setShakePos(int shake_pos); + + // Get the number of milliseconds since the program was started. + uint32 getMillis(); + + // Delay for a specified amount of milliseconds + void delayMillis(uint msecs); + + // Get the next event. + // Returns true if an event was retrieved. + bool pollEvent(Event &event); + + // Set function that generates samples + bool setSoundCallback(SoundProc proc, void *param); + void clearSoundCallback(); + + // Determine the output sample rate. Audio data provided by the sound + // callback will be played using this rate. + int getOutputSampleRate() const; + + // Quit + void quit(); + + // Add a callback timer + void setTimerCallback(TimerProc callback, int interval); + + // Mutex handling + MutexRef createMutex(); + void lockMutex(MutexRef mutex); + void unlockMutex(MutexRef mutex); + void deleteMutex(MutexRef mutex); + + // Overlay handling for the new menu system + void showOverlay(); + void hideOverlay(); + void clearOverlay(); + void grabOverlay(int16 *, int); + void copyRectToOverlay(const int16 *, int, int, int, int, int); + virtual int16 getHeight(); + virtual int16 getWidth(); + + virtual void grabPalette(byte *colors, uint start, uint num); + + // Set a window caption or any other comparable status display to the + // given value. + void setWindowCaption(const char *caption); + + static OSystem *create(int gfx_mode, bool full_screen); + +private: + typedef struct { + int x, y; + int w, h; + int hot_x, hot_y; + } MouseState; + + typedef struct { + int x, y, w, h; + } DirtyRect; + + enum { + MAX_NUMBER_OF_DIRTY_RECTS = 32 + }; + + void create_empty_cursor(); + void draw_mouse(DirtyRect *dout); + void undraw_mouse(); + void updateScreen_helper(const DirtyRect *d, DirtyRect *dout); + void blit_convert(const DirtyRect *d, uint8 *dst, int pitch); + void blit(const DirtyRect *d, uint16 *dst, int pitch); + + uint8 *_local_fb; + uint16 *_local_fb_overlay; + bool _overlay_visible; + + int _window_width, _window_height; + int _fb_width, _fb_height; + int _scumm_x, _scumm_y; + + uint16 *_palette16; + uint32 *_palette32; + + bool _palette_changed; + Display *_display; + int _screen, _depth; + uint8 _bytesPerPixel; + Window _window; + GC _black_gc; + XImage *_image; + pthread_t _sound_thread; + + struct timeval _start_time; + + int _fake_right_mouse; + int _report_presses; + int _current_shake_pos; + int _new_shake_pos; + DirtyRect _ds[MAX_NUMBER_OF_DIRTY_RECTS]; + int _num_of_dirty_rects; + + MouseState _oldMouseState, _curMouseState; + byte *_ms_buf; + bool _mouse_visible; + bool _mouse_state_changed; + byte _mouseKeycolor; + + uint32 _timer_duration, _timer_next_expiry; + bool _timer_active; + int (*_timer_callback) (int); +}; + +typedef struct { + OSystem::SoundProc sound_proc; + void *param; +} THREAD_PARAM; |