Logo Search packages:      
Sourcecode: scummvm version File versions  Download package

x11.cpp

/* ScummVM - Scumm Interpreter
 * Copyright (C) 2001  Ludvig Strigeus
 * Copyright (C) 2001-2005 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.
 *
 * $Header: /cvsroot/scummvm/scummvm/backends/x11/x11.cpp,v 1.38.2.1 2005/10/18 02:11:17 sev Exp $
 *
 */

/* 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 "backends/intern.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>

class OSystem_X11:public OSystem {
public:
      // 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, int overlaySize);

      // 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 hotspot_x, int hotspot_y, byte keycolor);

      // 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;

      // Initialise the specified CD drive for audio playback.
      bool openCD(int drive);

      // Poll cdrom status
      // Returns true if cd audio is playing
      bool pollCD();

      // Play cdrom audio track
      void playCD(int track, int num_loops, int start_frame, int duration);

      // Stop cdrom audio track
      void stopCD();

      // Update cdrom audio status
      void updateCD();

      // 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();

      // 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:
        OSystem_X11();

      typedef struct {
            int x, y, w, h;
      } dirty_square;

      void create_empty_cursor();
      void draw_mouse(dirty_square *dout);
      void undraw_mouse();
      void updateScreen_helper(const dirty_square * d, dirty_square * dout);
      void blit_convert(const dirty_square * 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 *palette;
      bool _palette_changed;
      Display *display;
      int screen, depth;
      Window window;
      GC black_gc;
      XImage *image;
      pthread_t sound_thread;

      int fake_right_mouse;
      int report_presses;
      int current_shake_pos;
      int new_shake_pos;
      struct timeval start_time;

      enum {
            MAX_NUMBER_OF_DIRTY_SQUARES = 32
      };
      dirty_square ds[MAX_NUMBER_OF_DIRTY_SQUARES];
      int num_of_dirty_square;

      typedef struct {
            int x, y;
            int w, h;
            int hot_x, hot_y;
      } mouse_state;
      mouse_state old_state, cur_state;
      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;

#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) {
            error("Error opening sound device !\n");
            exit(1);
      }
      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, &param) != 0) {
            error("Error in the SNDCTL_DSP_SETFRAGMENT ioctl !\n");
            exit(1);
      }
      param = AFMT_S16_LE;
      if (ioctl(sound_fd, SNDCTL_DSP_SETFMT, &param) == -1) {
            perror("Error in the SNDCTL_DSP_SETFMT ioctl !\n");
            exit(1);
      }
      if (param != AFMT_S16_LE) {
            error("AFMT_S16_LE not supported !\n");
            exit(1);
      }
      param = 2;
      if (ioctl(sound_fd, SNDCTL_DSP_CHANNELS, &param) == -1) {
            error("Error in the SNDCTL_DSP_CHANNELS ioctl !\n");
            exit(1);
      }
      if (param != 2) {
            error("Stereo mode not supported !\n");
            exit(1);
      }
      param = SAMPLES_PER_SEC;
      if (ioctl(sound_fd, SNDCTL_DSP_SPEED, &param) == -1) {
            perror("Error in the SNDCTL_DSP_SPEED ioctl !\n");
            exit(1);
      }
      if (param != SAMPLES_PER_SEC) {
            error("%d kHz not supported !\n", SAMPLES_PER_SEC);
            exit(1);
      }
      if (ioctl(sound_fd, SNDCTL_DSP_GETOSPACE, &info) != 0) {
            perror("SNDCTL_DSP_GETOSPACE");
            exit(-1);
      }

      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);
}

OSystem *OSystem_X11_create() {
      return OSystem_X11::create(0, 0);
}

OSystem *OSystem_X11::create(int gfx_mode, bool full_screen) {
      OSystem_X11 *syst = new OSystem_X11();
      return syst;
}

OSystem_X11::OSystem_X11() {
      char buf[512];
      XWMHints *wm_hints;
      XGCValues values;
      XTextProperty window_name;
      char *name = (char *)&buf;

      /* Some members initialization */
      fake_right_mouse = 0;
      report_presses = 1;
      current_shake_pos = 0;
      new_shake_pos = 0;
      _palette_changed = false;
      num_of_dirty_square = 0;
      _overlay_visible = false;
      _mouse_state_changed = true;
      _mouse_visible = true;
      _ms_buf = NULL;
      cur_state.x = 0;
      cur_state.y = 0;
      cur_state.hot_x = 0;
      cur_state.hot_y = 0;
      cur_state.w = 0;
      cur_state.h = 0;

      /* 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);
      if (depth != 16)
            error("Your screen depth is %ibit. Values other than 16bit 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);

      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);
}

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 {
      return NULL;      // TODO / FIXME
}

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(&current_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, int overlaySize) {
      static XShmSegmentInfo shminfo;

      fb_width = w;
      fb_height = h;

      if ((fb_width != 320) || (fb_height != 200)) {
            /* 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);
      }

      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);

      /* 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));
      palette = (uint16 *)calloc(256, 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) {
      const byte *data = colors;
      uint16 *pal = &(palette[start]);

      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_square < MAX_NUMBER_OF_DIRTY_SQUARES) {  \
    ds[num_of_dirty_square].x = xi;                   \
    ds[num_of_dirty_square].y = yi;                   \
    ds[num_of_dirty_square].w = wi;                   \
    ds[num_of_dirty_square].h = hi;                   \
    num_of_dirty_square++;                            \
  }

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_convert(const dirty_square * 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++ = palette[*ptr_src++];
            }
            ptr_dst += pitch - d->w;
            ptr_src += fb_width - d->w;
      }
}

void OSystem_X11::updateScreen_helper(const dirty_square * d, dirty_square * dout) {
      if (_overlay_visible == false) {
            blit_convert(d, (uint16 *) image->data, fb_width);
      } else {
            uint16 *ptr_src = local_fb_overlay + (fb_width * d->y) + d->x;
            uint16 *ptr_dst = ((uint16 *)image->data) + (fb_width * d->y) + d->x;
            int y;

            for (y = 0; y < d->h; y++) {
                  memcpy(ptr_dst, ptr_src, d->w * sizeof(uint16));
                  ptr_dst += fb_width;
                  ptr_src += fb_width;
            }
      }
      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 dirty_square ds_full = { 0, 0, fb_width, fb_height };
      dirty_square dout = { fb_width, fb_height, 0, 0 };

      if (_palette_changed) {
            full_redraw = true;
            num_of_dirty_square = 0;
            _palette_changed = false;
      } else if (num_of_dirty_square >= MAX_NUMBER_OF_DIRTY_SQUARES) {
            full_redraw = true;
            num_of_dirty_square = 0;
      }

      if (full_redraw) {
            updateScreen_helper(&ds_full, &dout);
            need_redraw = true;
      } else if ((num_of_dirty_square > 0) || (_mouse_state_changed == true)) {
            need_redraw = true;
            while (num_of_dirty_square > 0) {
                  num_of_dirty_square--;
                  updateScreen_helper(&(ds[num_of_dirty_square]), &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) {
}

void OSystem_X11::undraw_mouse() {
      AddDirtyRec(old_state.x, old_state.y, old_state.w, old_state.h);
}

void OSystem_X11::draw_mouse(dirty_square *dout) {
      _mouse_state_changed = false;

      if (_mouse_visible == false)
            return;

      int xdraw = cur_state.x - cur_state.hot_x;
      int ydraw = cur_state.y - cur_state.hot_y;
      int w = cur_state.w;
      int h = cur_state.h;
      int real_w;
      int real_h;

      uint16 *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 = ((uint16 *) image->data) + (ydraw * fb_width) + xdraw;

      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;

      old_state.x = xdraw;
      old_state.y = ydraw;
      old_state.w = real_w;
      old_state.h = real_h;

      while (real_h > 0) {
            int width = real_w;
            while (width > 0) {
                  byte color = *buf;
                  if (color != _mouseKeycolor) {
                        *dst = palette[color];
                  }
                  buf++;
                  dst++;
                  width--;
            }
            buf += w - real_w;
            dst += fb_width - real_w;
            real_h--;
      }
}

void OSystem_X11::set_mouse_pos(int x, int y) {
      if ((x != cur_state.x) || (y != cur_state.y)) {
            cur_state.x = x;
            cur_state.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) {
      cur_state.w = w;
      cur_state.h = h;
      cur_state.hot_x = hotspot_x;
      cur_state.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;
}

bool OSystem_X11::openCD(int drive) {
      return false;
}

bool OSystem_X11::pollCD() {
      return false;
}

void OSystem_X11::playCD(int track, int num_loops, int start_frame, int duration) {
}

void OSystem_X11::stopCD() {
}

void OSystem_X11::updateCD() {
}

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;
      dirty_square d = { 0, 0, fb_width, fb_height };
      AddDirtyRec(0, 0, fb_width, fb_height);
      blit_convert(&d, local_fb_overlay, fb_width);
}

void OSystem_X11::grabOverlay(int16 *dest, int pitch) {
      if (_overlay_visible == false)
            return;

      dirty_square d = { 0, 0, fb_width, fb_height };
      blit_convert(&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;
}


Generated by  Doxygen 1.6.0   Back to index