mirror of
https://github.com/libretro/scummvm.git
synced 2025-01-15 06:18:33 +00:00
7bb53f3bf1
get at least 640x400 window. And finally we have means of implementing nice looking GUI. Also updated all backends. If your backend has ability to run with 640x400 or 640x480 resolution then read patch tracker item to find out details. Other port maintainers shouldn't worry, as this patch doesn't affect them, they still get their 320x200. svn-id: r17055
1112 lines
27 KiB
C++
1112 lines
27 KiB
C++
/* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
*
|
|
* $Header$
|
|
*
|
|
*/
|
|
|
|
/* 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, ¶m) != 0) {
|
|
error("Error in the SNDCTL_DSP_SETFRAGMENT ioctl !\n");
|
|
exit(1);
|
|
}
|
|
param = AFMT_S16_LE;
|
|
if (ioctl(sound_fd, SNDCTL_DSP_SETFMT, ¶m) == -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, ¶m) == -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, ¶m) == -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(¤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, 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;
|
|
}
|
|
|