links/pmshell.c

2953 lines
78 KiB
C

/* pmshell.c
* PMShell graphics driver
* (c) 2002 Mikulas Patocka
* This file is a part of the Links program, released under GPL.
*/
#include "cfg.h"
/*#define PM_DEBUG*/
#ifdef PM_DEBUG
#define debug_call(x) do { printf("%016llx, %d: ", (unsigned long long)get_time(), _gettid()); printf x; putchar('\n'); fflush(stdout); } while (0)
#else
#define debug_call(x)
#endif
#ifdef GRDRV_PMSHELL
#include "links.h"
static long pm_get_color(int rgb);
extern struct graphics_driver pmshell_driver;
#ifdef OS2
#define INCL_DOS
#define INCL_DOSERRORS
#define INCL_GPI
#define INCL_WIN
#include <os2.h>
#include <sys/builtin.h>
#include <sys/fmutex.h>
#define PM_SPAWN_SUBPROC
/* Links on OS/2 crashes with 24-bit bpp and bitmaps wider than 10921 pixels,
apparently there is something that expects 15-bit line stride in bitmap
handling code in pmshell.
We set this limit lower, to 128, so that we don't exhaust shared memory with
big images */
#define OS2_MAX_BITMAP_SIZE 128
/* GpiDrawBits can't draw images with more than 32767 pixes in X direction.
We'd better draw them line-by-line */
#define OS2_MAX_LINE_SIZE 8191
extern PPIB os2_pib;
static ULONG pm_hidden_frame = 0;
static ULONG pm_frame = (FCF_TITLEBAR | FCF_SYSMENU | FCF_SIZEBORDER | FCF_MINMAX | FCF_SHELLPOSITION | FCF_TASKLIST | FCF_NOBYTEALIGN);
static HAB hab_disp;
static HAB hab;
static HMQ hmq;
static HDC hdc_mem;
static HPS hps_mem;
static HWND hwnd_hidden;
static HPS hps_hidden;
static HPOINTER icon;
static HPAL hpal;
#define pm_use_palette (hpal != NULLHANDLE && pmshell_driver.param->palette_mode)
static _fmutex pm_mutex;
static HEV pm_sem;
static ULONG pm_event_dummy;
static int pm_lock_init(void)
{
if (_fmutex_create(&pm_mutex, 0))
goto r0;
if (DosCreateEventSem(NULL, &pm_sem, 0, 0))
goto r1;
return 0;
r1:
_fmutex_checked_close(&pm_mutex);
r0:
return -1;
}
static void pm_lock_close(void)
{
APIRET r;
r = DosCloseEventSem(pm_sem);
if (r && r != ERROR_SEM_BUSY)
fatal_exit("DoscloseEventSem failed: %ld", (long)r);
_fmutex_checked_close(&pm_mutex);
}
static inline void pm_lock(void)
{
_fmutex_request(&pm_mutex, _FMR_IGNINT);
}
static inline void pm_unlock(void)
{
_fmutex_release(&pm_mutex);
}
static inline void pm_event_wait(void)
{
APIRET r;
wait_again:
r = DosWaitEventSem(pm_sem, SEM_INDEFINITE_WAIT);
if (r == ERROR_INTERRUPT)
goto wait_again;
if (r)
fatal_exit("DosWaitEventSem failed: %ld", (long)r);
if ((r = DosResetEventSem(pm_sem, &pm_event_dummy)))
fatal_exit("DosResetEventSem failed: %ld", (long)r);
}
static inline void pm_event_signal(void)
{
APIRET r;
if ((r = DosPostEventSem(pm_sem)))
fatal_exit("DosPostEventSem failed: %ld", (long)r);
}
static inline void SetCapture(HWND hwnd)
{
WinSetCapture(HWND_DESKTOP, hwnd);
}
static inline void ReleaseCapture(void)
{
WinSetCapture(HWND_DESKTOP, NULLHANDLE);
}
#define PM_START_DRAW() do { } while (0)
#define PM_UNFLUSH() do { } while (0)
#endif
#ifdef WIN
/*#define UNICODE*/
/*#define NO_STRICT*/
#include <windows.h>
#ifdef HAVE_WINDOWSX_H
#include <windowsx.h>
#endif
#include <pthread.h>
static HMODULE module_handle;
static pthread_t pthread_handle;
static int unicode_title_supported;
static int unicode_supported;
static ATOM window_class_atom;
static HDC screen_dc;
static HWND hwnd_hidden;
static HICON icon_big;
static HICON icon_small;
#define RECTL RECT
#define xLeft left
#define xRight right
#define yTop top
#define yBottom bottom
#define WM_BUTTON1DOWN WM_LBUTTONDOWN
#define WM_BUTTON1DBLCLK WM_LBUTTONDBLCLK
#define WM_BUTTON2DOWN WM_RBUTTONDOWN
#define WM_BUTTON2DBLCLK WM_RBUTTONDBLCLK
#define WM_BUTTON3DOWN WM_MBUTTONDOWN
#define WM_BUTTON3DBLCLK WM_MBUTTONDBLCLK
#define WM_BUTTON1UP WM_LBUTTONUP
#define WM_BUTTON2UP WM_RBUTTONUP
#define WM_BUTTON3UP WM_MBUTTONUP
#ifndef WM_XBUTTONDOWN
#define WM_XBUTTONDOWN 0x20b
#endif
#ifndef WM_XBUTTONDBLCLK
#define WM_XBUTTONDBLCLK 0x20d
#endif
#ifndef WM_XBUTTONUP
#define WM_XBUTTONUP 0x20c
#endif
#ifndef XBUTTON1
#define XBUTTON1 0x0001
#endif
#ifndef XBUTTON2
#define XBUTTON2 0x0002
#endif
#ifndef GET_X_LPARAM
#define GET_X_LPARAM(lp) ((short)LOWORD(lp))
#endif
#ifndef GET_Y_LPARAM
#define GET_Y_LPARAM(lp) ((short)HIWORD(lp))
#endif
static HANDLE winapi_semaphore;
static HANDLE winapi_event;
static CRITICAL_SECTION winapi_draw_lock;
static tcount winapi_draw_progress;
static int pm_lock_init(void)
{
winapi_semaphore = CreateSemaphoreA(NULL, 1, 1, NULL);
if (!winapi_semaphore)
goto r0;
winapi_event = CreateEventA(NULL, FALSE, FALSE, NULL);
if (!winapi_event)
goto r1;
InitializeCriticalSection(&winapi_draw_lock);
winapi_draw_progress = 0;
return 0;
r1:
if (!CloseHandle(winapi_semaphore))
fatal_exit("CloseHandle failed");
r0:
return -1;
}
static void pm_lock_close(void)
{
DeleteCriticalSection(&winapi_draw_lock);
if (!CloseHandle(winapi_event))
fatal_exit("CloseHandle failed");
if (!CloseHandle(winapi_semaphore))
fatal_exit("CloseHandle failed");
}
static inline void pm_lock(void)
{
if (WaitForSingleObject(winapi_semaphore, INFINITE) == WAIT_FAILED)
fatal_exit("WaitForSingleObject failed");
}
static inline void pm_unlock(void)
{
if (!ReleaseSemaphore(winapi_semaphore, 1, NULL))
fatal_exit("ReleaseSemaphore failed");
}
static inline void pm_event_wait(void)
{
if (WaitForSingleObject(winapi_event, INFINITE) == WAIT_FAILED)
fatal_exit("WaitForSingleObject failed");
}
static inline void pm_event_signal(void)
{
if (!SetEvent(winapi_event))
fatal_exit("SetEvent failed");
}
static void pm_do_flush(void *ignore)
{
if (winapi_draw_progress & 1) {
if (!GdiFlush())
{/*error("GdiFlush failed: %u", (unsigned)GetLastError());*/}
EnterCriticalSection(&winapi_draw_lock);
winapi_draw_progress++;
LeaveCriticalSection(&winapi_draw_lock);
}
}
ATTR_NOINLINE static void pm_start_draw_slow(void)
{
register_bottom_half(pm_do_flush, NULL);
EnterCriticalSection(&winapi_draw_lock);
winapi_draw_progress++;
LeaveCriticalSection(&winapi_draw_lock);
}
static inline void PM_START_DRAW(void)
{
if (!(winapi_draw_progress & 1))
pm_start_draw_slow();
}
#define PM_UNFLUSH() unregister_bottom_half(pm_do_flush, NULL)
#endif
#define PM_ALLOC_ZERO 1
#define PM_ALLOC_MAYFAIL 2
#if defined(WIN) && !defined(USE_WIN32_HEAP)
/* space allocated with malloc may span multiple mapped areas
and SetDIBitsToDevice doesn't like it. So use HeapAlloc/HeapFree */
static void *pm_alloc(size_t size, int flags)
{
void *data;
again:
data = HeapAlloc(GetProcessHeap(), flags & PM_ALLOC_ZERO ? HEAP_ZERO_MEMORY : 0, size);
if (!data) {
if (out_of_memory(0, flags & PM_ALLOC_MAYFAIL ? NULL : cast_uchar "pm_alloc", size))
goto again;
}
return data;
}
static void pm_free(void *ptr)
{
HeapFree(GetProcessHeap(), 0, ptr);
}
#else
static void *pm_alloc(size_t size, int flags)
{
if (!(flags & PM_ALLOC_MAYFAIL)) {
if (!(flags & PM_ALLOC_ZERO))
return mem_alloc(size);
else
return mem_calloc(size);
} else {
if (!(flags & PM_ALLOC_ZERO))
return mem_alloc_mayfail(size);
else
return mem_calloc_mayfail(size);
}
}
#define pm_free mem_free
#endif
static BITMAPINFO *pm_bitmapinfo;
static int icon_set;
#define pm_class_name "links"
#define pm_class_name_l L"links"
#define E_KEY 1
#define E_MOUSE 2
#define E_REDRAW 3
#define E_RESIZE 4
struct pm_event {
list_entry_1st
int type;
int x1, y1, x2, y2;
list_entry_last
};
struct pm_window {
list_entry_1st
int x, y;
unsigned char in;
unsigned char minimized;
struct pm_window *nxt;
struct pm_window **prv;
#ifdef OS2
HPS ps;
HWND h;
#endif
#ifdef WIN
HDC dc;
HDC new_dc;
HDC old_dc;
HPEN pen_orig;
HPEN pen_cache;
HBRUSH brush_cache;
int pen_cache_color;
int brush_cache_color;
tcount drawing_cookie;
int timer_active;
#endif
HWND hc;
HRGN rgn;
struct graphics_device *dev;
int button;
unsigned long lastpos;
struct list_head queue;
list_entry_last
};
#define WIN_HASH 64
static int HASH_VALUE(HWND hw)
{
return (unsigned long)hw & (WIN_HASH - 1);
}
static struct pm_window *pm_windows[WIN_HASH];
static void pm_hash_window(struct pm_window *win)
{
int pos = HASH_VALUE(win->hc);
win->prv = &pm_windows[pos];
if ((win->nxt = pm_windows[pos])) pm_windows[pos]->prv = &win->nxt;
pm_windows[pos] = win;
}
static void pm_unhash_window(struct pm_window *win)
{
if (win->nxt) win->nxt->prv = win->prv;
*win->prv = win->nxt;
}
static inline struct pm_window *pm_lookup_window(HWND h)
{
struct pm_window *win;
for (win = pm_windows[HASH_VALUE(h)]; win && win->hc != h; win = win->nxt) ;
return win;
}
#define pm_win(dev) ((struct pm_window *)dev->driver_data)
static int pm_pipe[2];
static unsigned char *pm_status;
static int pm_cp;
static struct list_head pm_event_windows = { &pm_event_windows, &pm_event_windows };
static unsigned char pm_flush_font_cache = 0;
static void pm_send_event(struct pm_window *win, int t, int x1, int y1, int x2, int y2)
{
/* must be called with pm_lock */
struct pm_event *ev;
if ((ev = malloc(sizeof(struct pm_event)))) {
ev->type = t;
ev->x1 = x1, ev->y1 = y1;
ev->x2 = x2, ev->y2 = y2;
if (!win->in) {
if (list_empty(pm_event_windows)) {
int wr;
EINTRLOOP(wr, write(pm_pipe[1], "x", 1));
}
add_to_list(pm_event_windows, win);
win->in = 1;
}
add_to_list(win->queue, ev);
}
}
static void pm_send_mouse_event(struct pm_window *win, int x1, int y1, int b)
{
if (!list_empty(win->queue)) {
struct pm_event *last = list_struct(win->queue.next, struct pm_event);
if (last->type == E_MOUSE && last->x2 == b) {
last->x1 = x1;
last->y1 = y1;
return;
}
}
pm_send_event(win, E_MOUSE, x1, y1, b, 0);
}
static void pm_cancel_event(struct pm_window *win, int t, struct pm_event **pev)
{
struct pm_event *ev;
struct list_head *lev;
if (pev) *pev = NULL;
foreachback(struct pm_event, ev, lev, win->queue) if (ev->type == t) {
if (pev) *pev = ev;
else {
del_from_list(ev);
free(ev);
}
return;
}
}
static void pm_resize(struct pm_window *win, RECTL *r)
{
struct pm_event *ev;
win->x = r->xRight;
win->y = r->yTop;
pm_cancel_event(win, E_REDRAW, NULL);
pm_cancel_event(win, E_RESIZE, &ev);
if (ev) {
ev->x2 = r->xRight;
ev->y2 = r->yTop;
} else pm_send_event(win, E_RESIZE, 0, 0, r->xRight, r->yTop);
}
static void pm_redraw(struct pm_window *win, RECTL *r)
{
struct pm_event *ev;
pm_cancel_event(win, E_RESIZE, &ev);
if (ev) return;
pm_cancel_event(win, E_REDRAW, &ev);
if (ev) {
if (r->xLeft < ev->x1) ev->x1 = r->xLeft;
if (r->xRight > ev->x2) ev->x2 = r->xRight;
if (win->y - r->yTop < ev->y1) ev->y1 = win->y - r->yTop;
if (win->y - r->yBottom > ev->y2) ev->y2 = win->y - r->yBottom;
return;
}
pm_send_event(win, E_REDRAW, r->xLeft, win->y - r->yTop, r->xRight, win->y - r->yBottom);
}
#ifdef OS2
#define N_VK 0x42
static struct os2_key pm_vk_table[N_VK] = {
{0, 0}, {0, 0}, {0, 0}, {0, 0}, {KBD_CTRL_C, 0}, {KBD_BS, 0}, {KBD_TAB, 0}, {KBD_TAB, KBD_SHIFT},
{KBD_ENTER, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {KBD_ESC, 0},
{' ', 0}, {KBD_PAGE_UP, 0}, {KBD_PAGE_DOWN, 0}, {KBD_END, 0}, {KBD_HOME, 0}, {KBD_LEFT, 0}, {KBD_UP, 0}, {KBD_RIGHT, 0},
{KBD_DOWN, 0}, {0, 0}, {KBD_INS, 0}, {KBD_DEL, 0}, {0, 0}, {0, 0}, {KBD_ENTER, 0}, {0, 0},
{KBD_F1, 0}, {KBD_F2, 0}, {KBD_F3, 0}, {KBD_F4, 0}, {KBD_F5, 0}, {KBD_F6, 0}, {KBD_F7, 0}, {KBD_F8, 0},
{KBD_F9, 0}, {KBD_F10, 0}, {KBD_F11, 0}, {KBD_F12, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},
{0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},
{0, 0}, {KBD_DEL, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},
{0, 0}, {0, 0}
};
#endif
static int win_x(struct pm_window *win)
{
#ifdef OS2
int x = (short)(win->lastpos & 0xffff);
#endif
#ifdef WIN
int x = GET_X_LPARAM(win->lastpos);
#endif
if (x >= win->x) x = win->x - 1;
if (x < 0) x = 0;
return x;
}
static int win_y(struct pm_window *win)
{
#ifdef OS2
int y = (short)(win->y - (win->lastpos >> 16));
#endif
#ifdef WIN
int y = GET_Y_LPARAM(win->lastpos);
#endif
if (y >= win->y) y = win->y - 1;
if (y < 0) y = 0;
return y;
}
static void pm_user_msg(unsigned long msg, void *mp1, void *mp2);
#ifdef OS2
static MRESULT EXPENTRY pm_window_proc(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
#define mouse_pos ((unsigned)mp1)
#endif
#ifdef WIN
static LRESULT CALLBACK pm_window_proc(HWND hwnd, UINT msg, WPARAM mp1, LPARAM mp2)
#define mouse_pos ((unsigned long)mp2)
#endif
{
#ifdef OS2
MRESULT ret;
int k_usch;
int scancode;
#endif
int flags;
long k_usvk;
long k_fsflags;
long key;
struct pm_window *win;
RECTL wr, ur;
debug_call(("T: pm_window_proc: %lx %lx %p %p", hwnd, msg, mp1, mp2));
if (hwnd == hwnd_hidden) {
debug_call(("T: pm_user_msg"));
pm_user_msg(msg, (void *)mp1, (void *)mp2);
} else switch (msg) {
#ifdef OS2
case WM_REALIZEPALETTE:
pm_lock();
if (pm_use_palette) {
if ((win = pm_lookup_window(hwnd))) {
ULONG pcclr;
HWND hc = win->hc;
HPS ps = win->ps;
LONG nc;
pm_unlock();
nc = WinRealizePalette(hc, ps, &pcclr);
if (nc == PAL_ERROR)
error("WinRealizePalette failed: %lx", WinGetLastError(hab_disp));
/*fprintf(stderr, "WinRealizePalette: %lu, %ld\n", pcclr, nc);*/
pm_lock();
}
}
if ((win = pm_lookup_window(hwnd))) {
pm_flush_font_cache = 1;
if (!WinQueryWindowRect(win->h, &wr)) {
error("WinQueryWindowRect failed: %lx", WinGetLastError(hab_disp));
memset(&wr, 0, sizeof wr);
}
pm_redraw(win, &wr);
}
pm_unlock();
return 0;
#endif
case WM_PAINT:
debug_call(("T: WM_PAINT: pm_lock"));
pm_lock();
#ifdef OS2
debug_call(("T: WM_PAINT: WinQueryUpdateRect"));
if (!WinQueryUpdateRect(hwnd, &ur)) {
error("WinQueryUpdateRect failed: %lx", WinGetLastError(hab_disp));
memset(&ur, 0, sizeof ur);
}
debug_call(("T: WM_PAINT: WinQueryWindowRect"));
if (!WinQueryWindowRect(hwnd, &wr)) {
error("WinQueryWindowRect failed: %lx", WinGetLastError(hab_disp));
memset(&wr, 0, sizeof wr);
}
debug_call(("T: WM_PAINT: WinValidateRect"));
if (!WinValidateRect(hwnd, &ur, FALSE))
error("WinValidateRect failed: %lx", WinGetLastError(hab_disp));
#endif
#ifdef WIN
GetUpdateRect(hwnd, &ur, FALSE);
if (!GetClientRect(hwnd, &wr)) {
memset(&wr, 0, sizeof wr);
}
wr.yTop = wr.yBottom;
wr.yBottom = 0;
if (!ValidateRect(hwnd, &ur))
fatal_exit("ValidateRect failed");
#endif
debug_call(("T: WM_PAINT: pm_lookup_window"));
if (!(win = pm_lookup_window(hwnd))) {
debug_call(("T: WM_PAINT: pm_lookup_window failed"));
pm_unlock();
debug_call(("T: WM_PAINT: return 0"));
return 0;
}
if (win->minimized) {
debug_call(("T: WM_PAINT: win->minimized"));
pm_unlock();
debug_call(("T: WM_PAINT: break"));
break;
}
#ifdef WIN
ur.yTop = win->y - ur.yTop;
ur.yBottom = win->y - ur.yBottom;
#endif
if (wr.xRight != win->x || wr.yTop != win->y) {
#if 0
if (WinQueryWindowRect(win->h, &wr)) {
pm_default_window_width = wr.xRight;
pm_default_window_height = wr.yTop;
}
#endif
debug_call(("T: WM_PAINT: pm_resize"));
#ifdef WIN
KillTimer(win->hc, 0);
KillTimer(win->hc, 1);
#endif
pm_resize(win, &wr);
} else {
debug_call(("T: WM_PAINT: pm_redraw"));
pm_redraw(win, &ur);
}
debug_call(("T: WM_PAINT: pm_unlock"));
pm_unlock();
debug_call(("T: WM_PAINT: return 0"));
return 0;
#ifdef OS2
case WM_MINMAXFRAME:
debug_call(("T: WM_MINMAXFRAME: pm_lock"));
pm_lock();
debug_call(("T: WM_MINMAXFRAME: pm_lookup_window"));
if (!(win = pm_lookup_window(hwnd))) {
debug_call(("T: WM_MINMAXFRAME: pm_lookup_window failed"));
pm_unlock();
debug_call(("T: WM_MINMAXFRAME: return 0"));
return 0;
}
if (((SWP *)mp1)->fl & (SWP_HIDE | SWP_MINIMIZE))
win->minimized = 1;
else
win->minimized = 0;
debug_call(("T: WM_MINMAXFRAME: minimized: %d, win->minimized", win->minimized));
pm_unlock();
debug_call(("T: WM_MINMAXFRAME: break"));
break;
#endif
case WM_CLOSE:
debug_call(("T: WM_CLOSE"));
case WM_QUIT:
debug_call(("T: WM_QUIT: pm_lock"));
pm_lock();
debug_call(("T: WM_QUIT: pm_lookup_window"));
if (!(win = pm_lookup_window(hwnd))) {
debug_call(("T: WM_QUIT: pm_lookup_window failed"));
pm_unlock();
debug_call(("T: WM_QUIT: return 0"));
return 0;
}
debug_call(("T: WM_QUIT: pm_send_event"));
pm_send_event(win, E_KEY, KBD_CTRL_C, 0, 0, 0);
debug_call(("T: WM_QUIT: pm_unlock"));
pm_unlock();
debug_call(("T: WM_QUIT: return 0"));
return 0;
case WM_CHAR:
debug_call(("T: WM_CHAR"));
#ifdef WIN
case WM_SYSCHAR:
case WM_KEYDOWN:
case WM_SYSKEYDOWN:
#endif
#ifdef OS2
k_fsflags = (int)mp1;
scancode = ((unsigned long)mp1 >> 24) & 0xff;
k_usch = (int)mp2 & 0xffff;
k_usvk = ((int)mp2 >> 16) & 0xffff;
/*
* F1 keydown is lost for unknown reason --- so catch
* keyup instead.
*/
if ((k_fsflags & (KC_VIRTUALKEY | KC_CHAR)) == KC_VIRTUALKEY && k_usvk == 0x20)
k_fsflags ^= KC_KEYUP;
if (k_fsflags & (KC_KEYUP | KC_DEADKEY | KC_INVALIDCOMP)) {
debug_call(("T: WM_CHAR: return 0 @ 1"));
return 0;
}
flags = (k_fsflags & KC_SHIFT ? KBD_SHIFT : 0) | (k_fsflags & KC_CTRL ? KBD_CTRL : 0) | (k_fsflags & KC_ALT ? KBD_ALT : 0);
if (k_fsflags & KC_ALT && ((scancode >= 0x47 && scancode <= 0x49) || (scancode >= 0x4b && scancode <= 0x4d) || (scancode >= 0x4f && scancode <= 0x52))) {
debug_call(("T: WM_CHAR: return 0 @ 2"));
return 0;
}
if ((k_fsflags & (KC_VIRTUALKEY | KC_CHAR)) == KC_VIRTUALKEY) {
if (k_usvk < N_VK && (key = pm_vk_table[k_usvk].x)) {
flags |= pm_vk_table[k_usvk].y;
if (key == KBD_CTRL_C) flags &= ~KBD_CTRL;
goto s;
}
}
if (k_usch & 0xff) {
key = k_usch & 0xff;
if (!(flags & KBD_CTRL)) {
if (key == 0x0d) key = KBD_ENTER;
if (key == 0x08) key = KBD_BS;
if (key == 0x09) key = KBD_TAB;
if (key == 0x1b) key = KBD_ESC;
}
if (key >= 0 && key < ' ') key += '@', flags |= KBD_CTRL;
} else key = os2xtd[k_usch >> 8].x, flags |= os2xtd[k_usch >> 8].y;
if ((key & 0xdf) == 'C' && (flags & KBD_CTRL)) key = KBD_CTRL_C, flags &= ~KBD_CTRL;
s:
if (!key) {
debug_call(("T: WM_CHAR: return 0 @ 3"));
return 0;
}
/*if (key >= 0) flags &= ~KBD_SHIFT;*/
if (key >= 0x80 && pm_cp) {
if ((key = cp2u(key, pm_cp)) < 0) {
debug_call(("T: WM_CHAR: return 0 @ 4"));
return 0;
}
}
#endif
#ifdef WIN
k_fsflags = (long)mp2;
flags = 0;
if (k_fsflags & (1 << 29))
flags |= KBD_ALT;
if (msg == WM_KEYDOWN || msg == WM_SYSKEYDOWN) {
/*fprintf(stderr, "vk: %lx %lx\n", (long)mp1, (long)mp2);*/
k_usvk = (long)mp1;
switch (k_usvk) {
case VK_CANCEL: key = KBD_CTRL_C; break;
case VK_PRIOR: key = KBD_PAGE_UP; break;
case VK_NEXT: key = KBD_PAGE_DOWN; break;
case VK_END: key = KBD_END; break;
case VK_HOME: key = KBD_HOME; break;
case VK_LEFT: key = KBD_LEFT; break;
case VK_RIGHT: key = KBD_RIGHT; break;
case VK_DOWN: key = KBD_DOWN; break;
case VK_UP: key = KBD_UP; break;
case VK_INSERT: key = KBD_INS; break;
case VK_DELETE: key = KBD_DEL; break;
case VK_F1: key = KBD_F1; break;
case VK_F2: key = KBD_F2; break;
case VK_F3: key = KBD_F3; break;
case VK_F4: key = KBD_F4; break;
case VK_F5: key = KBD_F5; break;
case VK_F6: key = KBD_F6; break;
case VK_F7: key = KBD_F7; break;
case VK_F8: key = KBD_F8; break;
case VK_F9: key = KBD_F9; break;
case VK_F10: key = KBD_F10; break;
case VK_F11: key = KBD_F11; break;
case VK_F12: key = KBD_F12; break;
#ifdef VK_HELP
case VK_HELP: key = KBD_HELP; break;
#endif
#ifdef VK_APPS
case VK_APPS: key = KBD_MENU; break;
#endif
#ifdef VK_BROWSER_BACK
case VK_BROWSER_BACK: key = KBD_BACK; break;
#endif
#ifdef VK_BROWSER_FORWARD
case VK_BROWSER_FORWARD: key = KBD_FORWARD; break;
#endif
#ifdef VK_BROWSER_REFRESH
case VK_BROWSER_REFRESH: key = KBD_RELOAD; break;
#endif
#ifdef VK_BROWSER_STOP
case VK_BROWSER_STOP: key = KBD_STOP; break;
#endif
#ifdef VK_BROWSER_SEARCH
case VK_BROWSER_SEARCH: key = KBD_FIND; break;
#endif
#ifdef VK_BROWSER_FAVORITES
case VK_BROWSER_FAVORITES: key = KBD_BOOKMARKS; break;
#endif
#ifdef VK_BROWSER_HOME
case VK_BROWSER_HOME: key = KBD_OPEN; break;
#endif
default: return 0;
}
} else {
/*fprintf(stderr, "ch: %lx %lx\n", (long)mp1, (long)mp2);*/
key = (long)mp1;
if (key <= 0) return 0;
if (key > 0 && key < 0x20) {
if (key == 3) key = KBD_CTRL_C;
else if (key == 8) key = KBD_BS;
else if (key == 9) key = KBD_TAB;
else if (key == 10) key = KBD_ENTER;
else if (key == 13) key = KBD_ENTER;
else if (key == 27) key = KBD_ESC;
else if (key == 127) key = KBD_BS;
else {
key += 'A' - 1;
flags |= KBD_CTRL;
}
} else if (!unicode_supported) {
key = cp2u(key, pm_cp);
}
}
#endif
debug_call(("T: WM_CHAR: pm_lock"));
pm_lock();
debug_call(("T: WM_CHAR: pm_lookup_window"));
if (!(win = pm_lookup_window(hwnd))) {
debug_call(("T: WM_CHAR: pm_lookup_window failed"));
pm_unlock();
debug_call(("T: WM_CHAR: return 0"));
return 0;
}
debug_call(("T: WM_CHAR: pm_send_event"));
pm_send_event(win, E_KEY, key, flags, 0, 0);
debug_call(("T: WM_CHAR: pm_unlock"));
pm_unlock();
debug_call(("T: WM_CHAR: return 0"));
return 0;
case WM_BUTTON1DOWN:
case WM_BUTTON1DBLCLK:
flags = B_LEFT;
goto button_down;
case WM_BUTTON2DOWN:
case WM_BUTTON2DBLCLK:
flags = B_RIGHT;
goto button_down;
case WM_BUTTON3DOWN:
case WM_BUTTON3DBLCLK:
flags = B_MIDDLE;
goto button_down;
button_down:
debug_call(("T: BUTTON_DOWN: pm_lock"));
pm_lock();
debug_call(("T: BUTTON_DOWN: pm_lookup_window"));
if (!(win = pm_lookup_window(hwnd))) {
debug_call(("T: BUTTON_DOWN: pm_lookup_window failed"));
pm_unlock();
debug_call(("T: BUTTON_DOWN: break"));
break;
}
win->button |= 1 << flags;
win->lastpos = mouse_pos;
debug_call(("T: BUTTON_DOWN: pm_send_event (%d)", flags));
pm_send_event(win, E_MOUSE, win_x(win), win_y(win), B_DOWN | flags, 0);
debug_call(("T: BUTTON_DOWN: pm_unlock"));
pm_unlock();
debug_call(("T: BUTTON_DOWN: SetCapture"));
SetCapture(hwnd);
debug_call(("T: BUTTON_DOWN: break"));
break;
case WM_BUTTON1UP:
#ifdef OS2
case WM_BUTTON1MOTIONEND:
#endif
flags = B_LEFT;
goto button_up;
case WM_BUTTON2UP:
#ifdef OS2
case WM_BUTTON2MOTIONEND:
#endif
flags = B_RIGHT;
goto button_up;
case WM_BUTTON3UP:
#ifdef OS2
case WM_BUTTON3MOTIONEND:
#endif
flags = B_MIDDLE;
goto button_up;
button_up:
debug_call(("T: BUTTON_UP: pm_lock"));
pm_lock();
debug_call(("T: BUTTON_UP: pm_lookup_window"));
if (!(win = pm_lookup_window(hwnd))) {
debug_call(("T: BUTTON_UP: pm_lookup_window failed"));
pm_unlock();
debug_call(("T: BUTTON_UP: break"));
break;
}
if (msg == WM_BUTTON1UP) win->lastpos = mouse_pos;
if (win->button & (1 << flags)) {
debug_call(("T: BUTTON_UP: pm_send_event (%d)", flags));
pm_send_event(win, E_MOUSE, win_x(win), win_y(win), B_UP | flags, 0);
}
win->button &= ~(1 << flags);
debug_call(("T: BUTTON_UP: pm_unlock"));
pm_unlock();
if (!win->button) {
debug_call(("T: BUTTON_UP: ReleaseCapture"));
ReleaseCapture();
}
debug_call(("T: BUTTON_UP: break"));
break;
#ifdef WIN
case WM_XBUTTONDOWN:
case WM_XBUTTONDBLCLK:
if (HIWORD(mp1) == XBUTTON1)
flags = B_FOURTH;
else if (HIWORD(mp1) == XBUTTON2)
flags = B_FIFTH;
else break;
goto button_down;
case WM_XBUTTONUP:
if (HIWORD(mp1) == XBUTTON1)
flags = B_FOURTH;
else if (HIWORD(mp1) == XBUTTON2)
flags = B_FIFTH;
else break;
goto button_up;
#endif
case WM_MOUSEMOVE:
debug_call(("T: WM_MOUSEMOVE: pm_lock"));
pm_lock();
debug_call(("T: WM_MOUSEMOVE: pm_lookup_window"));
if (!(win = pm_lookup_window(hwnd))) {
debug_call(("T: WM_MOUSEMOVE: pm_lookup_window failed"));
pm_unlock();
debug_call(("T: WM_MOUSEMOVE: break"));
break;
}
#ifdef OS2
debug_call(("T: WM_MOUSEMOVE: %x -> WinQueryCapture", win->button));
if (win->button && WinQueryCapture(HWND_DESKTOP) != hwnd) {
debug_call(("T: WM_MOUSEMOVE: clear buttons"));
for (flags = B_LEFT; flags <= B_SIXTH; flags++)
if (win->button & (1 << flags))
pm_send_event(win, E_MOUSE, win_x(win), win_y(win), B_UP | flags, 0);
win->button = 0;
}
#endif
if (win->lastpos == mouse_pos) {
debug_call(("T: WM_MOUSEMOVE: pm_unlock"));
pm_unlock();
debug_call(("T: WM_MOUSEMOVE: break"));
break;
}
win->lastpos = mouse_pos;
debug_call(("T: WM_MOUSEMOVE: pm_send_mouse_event"));
pm_send_mouse_event(win, win_x(win), win_y(win),
(win->button ? B_DRAG : B_MOVE) |
(win->button & (1 << B_LEFT) ? B_LEFT :
win->button & (1 << B_MIDDLE) ? B_MIDDLE :
win->button & (1 << B_RIGHT) ? B_RIGHT :
win->button & (1 << B_FOURTH) ? B_FOURTH :
win->button & (1 << B_FIFTH) ? B_FIFTH :
win->button & (1 << B_SIXTH) ? B_SIXTH :
0));
debug_call(("T: WM_MOUSEMOVE: pm_unlock"));
pm_unlock();
debug_call(("T: WM_MOUSEMOVE: break"));
break;
#ifdef WIN
case WM_CAPTURECHANGED:
pm_lock();
if (!(win = pm_lookup_window(hwnd))) { pm_unlock(); break; }
if ((HWND)mp2 == win->hc) { pm_unlock(); break; }
for (flags = B_LEFT; flags <= B_SIXTH; flags++)
if (win->button & (1 << flags))
pm_send_event(win, E_MOUSE, win_x(win), win_y(win), B_UP | flags, 0);
win->button = 0;
pm_unlock();
break;
#endif
#ifdef OS2
case WM_VSCROLL:
debug_call(("T: VM_VSCROLL: pm_lock"));
pm_lock();
debug_call(("T: VM_VSCROLL: pm_lookup_window"));
if (!(win = pm_lookup_window(hwnd))) {
debug_call(("T: VM_VSCROLL: pm_lookup_window failed"));
pm_unlock();
debug_call(("T: VM_VSCROLL: break"));
break;
}
if ((unsigned long)mp2 == SB_LINEUP << 16 || (unsigned long)mp2 == SB_LINEDOWN << 16) {
debug_call(("T: VM_VSCROLL: pm_send_event"));
pm_send_event(win, E_MOUSE, win_x(win), win_y(win), ((unsigned long)mp2 == SB_LINEUP << 16 ? B_WHEELUP1 : B_WHEELDOWN1) | B_MOVE, 0);
}
debug_call(("T: VM_VSCROLL: pm_unlock"));
pm_unlock();
debug_call(("T: VM_VSCROLL: break"));
break;
case WM_HSCROLL:
debug_call(("T: VM_HSCROLL: pm_lock"));
pm_lock();
debug_call(("T: VM_HSCROLL: pm_lookup_window"));
if (!(win = pm_lookup_window(hwnd))) {
debug_call(("T: VM_HSCROLL: pm_lookup_window failed"));
pm_unlock();
debug_call(("T: VM_HSCROLL: break"));
break;
}
if ((unsigned long)mp2 == SB_LINELEFT << 16 || (unsigned long)mp2 == SB_LINERIGHT << 16) {
debug_call(("T: VM_HSCROLL: pm_send_event"));
pm_send_event(win, E_MOUSE, win_x(win), win_y(win), ((unsigned long)mp2 == SB_LINELEFT << 16 ? B_WHEELLEFT1 : B_WHEELRIGHT1) | B_MOVE, 0);
}
debug_call(("T: VM_HSCROLL: pm_unlock"));
pm_unlock();
debug_call(("T: VM_HSCROLL: break"));
break;
#endif
#ifdef WIN
case WM_MOUSEWHEEL:
case 0x020e:
flags = HIWORD(mp1);
if (!flags) break;
pm_lock();
if (!(win = pm_lookup_window(hwnd))) { pm_unlock(); break; }
pm_send_event(win, E_MOUSE, win_x(win), win_y(win), (flags > 0 && flags < 0x8000 ? (msg == WM_MOUSEWHEEL ? B_WHEELUP : B_WHEELRIGHT) : (msg == WM_MOUSEWHEEL ? B_WHEELDOWN: B_WHEELLEFT)) | B_MOVE, 0);
pm_unlock();
break;
#endif
#ifdef WIN
case WM_TIMER:
if (!GetClientRect(hwnd, &wr)) {
break;
}
wr.yTop = wr.yBottom;
wr.yBottom = 0;
pm_lock();
if ((win = pm_lookup_window(hwnd))) {
KillTimer(win->hc, 0);
KillTimer(win->hc, 1);
win->timer_active = 0;
pm_redraw(win, &wr);
}
pm_unlock();
break;
case WM_WINDOWPOSCHANGING:
if (!is_winnt())
break;
pm_lock();
if ((win = pm_lookup_window(hwnd)) && win->dc) {
EnterCriticalSection(&winapi_draw_lock);
win->drawing_cookie = winapi_draw_progress;
LeaveCriticalSection(&winapi_draw_lock);
}
pm_unlock();
break;
case WM_WINDOWPOSCHANGED:
if (!is_winnt())
break;
pm_lock();
if ((win = pm_lookup_window(hwnd)) && win->dc) {
int not_racing;
EnterCriticalSection(&winapi_draw_lock);
not_racing = win->drawing_cookie == winapi_draw_progress && !(winapi_draw_progress & 1);
LeaveCriticalSection(&winapi_draw_lock);
if (not_racing)
goto skip_windowposchanged;
if (!win->new_dc)
win->new_dc = GetDC(win->hc);
if (win->old_dc) {
if (!ReleaseDC(win->hc, win->old_dc))
error("ReleaseDC failed %u", (unsigned)GetLastError());
win->old_dc = NULL;
}
SetTimer(win->hc, 0, WIN32_REPAINT_IDLE_TIME, NULL);
if (!win->timer_active) {
SetTimer(win->hc, 1, WIN32_REPAINT_BUSY_TIME, NULL);
win->timer_active = 1;
}
}
skip_windowposchanged:
pm_unlock();
break;
#endif
}
#ifdef OS2
debug_call(("T: WinDefWindowProc: %lx %lx %p %p", hwnd, msg, mp1, mp2));
ret = WinDefWindowProc(hwnd, msg, mp1, mp2);
debug_call(("T: WinDefWindowProc returned %p", ret));
return ret;
#endif
#ifdef WIN
if (!unicode_supported)
return DefWindowProcA(hwnd, msg, mp1, mp2);
else
return DefWindowProcW(hwnd, msg, mp1, mp2);
#endif
}
static int pm_thread_shutdown;
#define MSG_CREATE_WINDOW (WM_USER + 0)
#define MSG_DELETE_WINDOW (WM_USER + 1)
#define MSG_SET_WINDOW_TITLE (WM_USER + 2)
#define MSG_SHUTDOWN_THREAD (WM_USER + 3)
static void pm_user_msg(unsigned long msg, void *mp1, void *mp2)
{
struct pm_window *win;
switch (msg) {
case MSG_CREATE_WINDOW:
win = mp1;
#ifdef OS2
debug_call(("T: WinCreateStdWindow"));
win->h = WinCreateStdWindow(HWND_DESKTOP, 0, &pm_frame, pm_class_name, "Links", 0, 0, 0, &win->hc);
debug_call(("T: WinCreateStdWindow: %lx", win->h));
if (win->h != NULLHANDLE) {
if (icon != NULLHANDLE) {
debug_call(("T: WinSendMsg"));
WinSendMsg(win->h, WM_SETICON, (void *)icon, 0);
}
debug_call(("T: WinSetWindowPos"));
if (!WinSetWindowPos(win->h, HWND_TOP, 0, 0, 0, 0, SWP_ACTIVATE | SWP_SHOW | SWP_ZORDER))
error("WinSetWindowPos failed: %lx", WinGetLastError(hab_disp));
}
#endif
#ifdef WIN
if (unicode_supported) {
win->hc = CreateWindowExW(WS_EX_APPWINDOW | WS_EX_LEFT, (void *)(unsigned long)window_class_atom, L"Links", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, module_handle, NULL);
} else {
win->hc = CreateWindowExA(WS_EX_APPWINDOW | WS_EX_LEFT, (void *)(unsigned long)window_class_atom, "Links", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, module_handle, NULL);
}
if (!win->hc)
goto fail_createwindowex;
if ((win->dc = GetDC(win->hc)) == NULL)
goto fail_getdc;
if (unicode_supported) {
if (icon_big != NULL) SendMessageW(win->hc, WM_SETICON, ICON_BIG, (LPARAM)icon_big);
if (icon_small != NULL) SendMessageW(win->hc, WM_SETICON, ICON_SMALL, (LPARAM)icon_small);
} else {
if (icon_big != NULL) SendMessageA(win->hc, WM_SETICON, ICON_BIG, (LPARAM)icon_big);
if (icon_small != NULL) SendMessageA(win->hc, WM_SETICON, ICON_SMALL, (LPARAM)icon_small);
}
ShowWindow(win->hc, SW_SHOW);
/*SetWindowPos(win->hc, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);*/
SetForegroundWindow(win->hc);
#endif
debug_call(("T: pm_lock"));
pm_lock();
debug_call(("T: pm_event_signal"));
pm_event_signal();
debug_call(("T: break"));
break;
case MSG_DELETE_WINDOW:
win = mp1;
#ifdef OS2
debug_call(("T: WinDestroyWindow"));
if (!WinDestroyWindow(win->h))
fatal_exit("WinDestroyWindow failed");
#endif
#ifdef WIN
if (win->dc) {
if (!ReleaseDC(win->hc, win->dc))
error("ReleaseDC failed %u", (unsigned)GetLastError());
win->dc = NULL;
}
if (win->old_dc) {
if (!ReleaseDC(win->hc, win->old_dc))
error("ReleaseDC failed %u", (unsigned)GetLastError());
win->old_dc = NULL;
}
if (win->new_dc) {
if (!ReleaseDC(win->hc, win->new_dc))
error("ReleaseDC failed %u", (unsigned)GetLastError());
win->new_dc = NULL;
}
fail_getdc:
if (!DestroyWindow(win->hc))
fatal_exit("DestroyWindow failed");
fail_createwindowex:
win->hc = NULL;
#endif
debug_call(("T: pm_lock"));
pm_lock();
debug_call(("T: pm_event_signal"));
pm_event_signal();
debug_call(("T: break"));
break;
case MSG_SET_WINDOW_TITLE:
win = mp1;
#ifdef OS2
debug_call(("T: WinSetWindowText"));
WinSetWindowText(win->h, mp2);
#endif
#ifdef WIN
if (unicode_supported && unicode_title_supported)
SetWindowTextW(win->hc, mp2);
else
SetWindowTextA(win->hc, mp2);
#endif
debug_call(("T: pm_lock"));
pm_lock();
debug_call(("T: pm_event_signal"));
pm_event_signal();
debug_call(("T: break"));
break;
case MSG_SHUTDOWN_THREAD:
debug_call(("T: pm_thread_shutdown"));
pm_thread_shutdown = 1;
break;
}
}
static void pm_send_msg(int msg, void *param, void *param2)
{
#ifdef OS2
debug_call(("M: calling WinPostMsg(%d, %p, %p)", msg, param, param2));
while (!WinPostMsg(hwnd_hidden, msg, (MPARAM)param, (MPARAM)param2)) {
debug_call(("M: WinPostMsg failed: %lx", WinGetLastError(hab)));
portable_sleep(1000);
}
debug_call(("M: WinPostMsg succeeded"));
#endif
#ifdef WIN
BOOL r;
if (!GdiFlush())
{/*error("GdiFlush failed: %u", (unsigned)GetLastError());*/}
retry:
if (!unicode_supported)
r = PostMessageA(hwnd_hidden, msg, (unsigned long)param, (unsigned long)param2);
else
r = PostMessageW(hwnd_hidden, msg, (unsigned long)param, (unsigned long)param2);
if (!r) {
DWORD err = GetLastError();
if (err == ERROR_NOT_ENOUGH_QUOTA) {
portable_sleep(1000);
goto retry;
}
fatal_exit("PostMessage failed: %x", (unsigned)GetLastError());
}
#endif
pm_event_wait();
debug_call(("M: pm_event_wait succeeded"));
#ifdef WIN
if (msg == MSG_SHUTDOWN_THREAD) {
if (pthread_join(pthread_handle, NULL))
fatal_exit("pthread_join failed");
}
#endif
}
static void pm_dispatcher(void *p)
{
#ifdef OS2
QMSG msg;
HWND hwnd_hidden_h;
#endif
#ifdef WIN
MSG msg;
HCURSOR hcursor;
WNDCLASSEXW class_w;
WNDCLASSEXA class_a;
#endif
pm_status = NULL;
#ifdef OS2
/*DosSetPriority(PRTYS_THREAD, PRTYC_FOREGROUNDSERVER, 1, 0);*/
/*DosSetPriority(PRTYS_THREAD, PRTYC_TIMECRITICAL, 1, 0);*/
DosSetPriority(PRTYS_THREAD, PRTYC_NOCHANGE, 1, 0);
if ((hab_disp = WinInitialize(0)) == NULLHANDLE) {
pm_status = cast_uchar "WinInitialize failed in pm thread.\n";
goto os2_fail0;
}
if ((hmq = WinCreateMsgQueue(hab_disp, 0)) == NULLHANDLE) {
ERRORID e = WinGetLastError(hab_disp);
if ((e & 0xffff) == PMERR_NOT_IN_A_PM_SESSION) pm_status = cast_uchar "Not in a pmshell session.\n";
else pm_status = cast_uchar "WinCreateMsgQueue failed in pm thread.\n";
goto os2_fail1;
}
if ((pm_cp = WinQueryCp(hmq))) {
unsigned char a[12];
snprint(a, 12, pm_cp);
if ((pm_cp = get_cp_index(a)) < 0 || pm_cp == utf8_table) pm_cp = 0;
}
/*{
ULONG cp_list[100];
int n, i;
debug("WinQueryCp: %d", WinQueryCp(hmq));
n = WinQueryCpList(hab_disp, 100, cp_list);
debug("%d", n);
for (i = 0; i < n; i++) fprintf(stderr, "%d, ", cp_list[i]);
}*/
if (WinRegisterClass(hab_disp, pm_class_name, pm_window_proc, CS_SIZEREDRAW, 0) == FALSE) {
pm_status = cast_uchar "WinRegisterClass failed for.\n";
goto os2_fail2;
}
if ((hwnd_hidden_h = WinCreateStdWindow(HWND_DESKTOP, 0, &pm_hidden_frame, pm_class_name, NULL, 0, 0, 0, &hwnd_hidden)) == NULLHANDLE) {
pm_status = cast_uchar "Could not create hidden window.\n";
goto os2_fail3;
}
if ((hps_hidden = WinGetPS(hwnd_hidden)) == NULLHANDLE) {
pm_status = cast_uchar "Could not get hidden window ps.\n";
goto os2_fail4;
}
#if 0
{
int c, d;
LONG colors[256];
c = GpiQueryRealColors(hps_hidden, 0, 0, 256, colors);
if (c == GPI_ALTERROR) {
error("GpiQueryRealColors failed: %lx", WinGetLastError(hab_disp));
} else {
for (d = 0; d < c; d++)
fprintf(stderr, "color[%03d] = %06lx\n", d, colors[d]);
}
}
#endif
#endif
#ifdef WIN
{
unsigned long (WINAPI *f_SetThreadDpiAwarenessContext)(unsigned long ctx);
f_SetThreadDpiAwarenessContext = (void *)GetProcAddress(GetModuleHandleA("user32.dll"), "SetThreadDpiAwarenessContext");
/*debug("result: %p", f_SetThreadDpiAwarenessContext);*/
if (f_SetThreadDpiAwarenessContext) {
f_SetThreadDpiAwarenessContext(-3);
}
}
pm_cp = get_windows_cp(0);
hcursor = LoadCursorA(0, (void *)IDC_ARROW);
memset(&class_w, 0, sizeof class_w);
class_w.cbSize = sizeof class_w;
class_w.style = CS_HREDRAW | CS_VREDRAW;
class_w.lpfnWndProc = pm_window_proc;
class_w.hInstance = module_handle;
class_w.hCursor = hcursor;
class_w.lpszClassName = pm_class_name_l;
window_class_atom = RegisterClassExW(&class_w);
if (window_class_atom) {
OSVERSIONINFO v;
unicode_supported = 1;
unicode_title_supported = 0;
v.dwOSVersionInfoSize = sizeof v;
if (is_winnt() && GetVersionEx(&v) && v.dwMajorVersion >= 5)
unicode_title_supported = 1;
} else {
unicode_supported = 0;
unicode_title_supported = 0;
memset(&class_a, 0, sizeof class_a);
class_a.cbSize = sizeof class_a;
class_a.style = CS_HREDRAW | CS_VREDRAW;
class_a.lpfnWndProc = pm_window_proc;
class_a.hInstance = module_handle;
class_a.hCursor = hcursor;
class_a.lpszClassName = pm_class_name;
window_class_atom = RegisterClassExA(&class_a);
}
if (!window_class_atom) {
pm_status = cast_uchar "Unable to register window class.\n";
goto win32_fail;
}
if (unicode_supported) {
hwnd_hidden = CreateWindowExW(WS_EX_LEFT, (void *)(unsigned long)window_class_atom, L"Links", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, module_handle, NULL);
} else {
hwnd_hidden = CreateWindowExA(WS_EX_LEFT, (void *)(unsigned long)window_class_atom, "Links", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, module_handle, NULL);
}
if (!hwnd_hidden) {
pm_status = cast_uchar "Could not create hidden window.\n";
goto win32_fail;
}
if (!unicode_supported)
PeekMessageA(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
else
PeekMessageW(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
#endif
pm_event_signal();
while (!pm_thread_shutdown) {
#ifdef OS2
/*BOOL ret;
debug_call(("T: calling WinPeekMsg"));
ret = WinPeekMsg(hab_disp, &msg, 0L, 0, 0, PM_REMOVE);
debug_call(("T: WinPeekMsg: %ld", ret));
if (!ret) {
debug_call(("T: calling WinWaitMsg"));
ret = WinWaitMsg(hab_disp, 0, 0);
debug_call(("T: WinWaitMsg: %ld", ret));
continue;
}*/
debug_call(("T: calling WinGetMsg"));
WinGetMsg(hab_disp, &msg, 0L, 0, 0);
debug_call(("T: WinGetMsg: %lx, %lx, %p, %p, %lx, %lx.%lx, %lx", msg.hwnd, msg.msg, msg.mp1, msg.mp2, msg.time, msg.ptl.x, msg.ptl.y, msg.reserved));
WinDispatchMsg(hab_disp, &msg);
#endif
#ifdef WIN
if (!unicode_supported)
GetMessageA(&msg, NULL, 0, 0);
else
GetMessageW(&msg, NULL, 0, 0);
TranslateMessage(&msg);
if (!unicode_supported)
DispatchMessageA(&msg);
else
DispatchMessageW(&msg);
#endif
}
#ifdef OS2
if (!WinReleasePS(hps_hidden))
error("WinReleasePS failed: %lx", WinGetLastError(hab_disp));
os2_fail4:
if (!WinDestroyWindow(hwnd_hidden_h))
error("WinDestroyWindow failed: %lx", WinGetLastError(hab_disp));
os2_fail3:
os2_fail2:
if (!WinDestroyMsgQueue(hmq))
error("WinDestroyMsgQueue failed: %lx", WinGetLastError(hab_disp));
os2_fail1:
if (!WinTerminate(hab_disp))
error("WinTerminate failed: %lx", WinGetLastError(hab_disp));
os2_fail0:
#endif
#ifdef WIN
if (!DestroyWindow(hwnd_hidden))
error("DestroyWindow failed: %u", (unsigned)GetLastError());
win32_fail:
#endif
pm_event_signal();
return;
}
#ifdef WIN
static void *pm_dispatcher_win32(void *p)
{
pm_dispatcher(NULL);
return NULL;
}
#endif
#ifdef WIN
static void pm_release_cached_objects(struct pm_window *win)
{
if (win->pen_orig) {
if (!SelectObject(win->dc, win->pen_orig))
error("SelectObject failed for original pen %u", (unsigned)GetLastError());
win->pen_orig = NULL;
}
if (win->pen_cache) {
if (!DeleteObject(win->pen_cache))
error("DeleteObject failed for pen cache %u", (unsigned)GetLastError());
win->pen_cache = NULL;
win->pen_cache_color = -1;
}
if (win->brush_cache) {
if (!DeleteObject(win->brush_cache))
error("DeleteObject failed for brush cache %u", (unsigned)GetLastError());
win->brush_cache = NULL;
win->brush_cache_color = -1;
}
if (!GdiFlush())
{/*error("GdiFlush failed %u", (unsigned)GetLastError());*/}
}
#endif
static void pm_handler(void *p)
{
unsigned char flush_font;
unsigned char c;
struct pm_window *win = NULL;
struct pm_event *ev = NULL;
debug_call(("M: pm_handler: pm_lock"));
pm_lock();
debug_call(("M: pm_handler: pm_locked"));
if (!list_empty(pm_event_windows)) {
win = list_struct(pm_event_windows.prev, struct pm_window);
if (!list_empty(win->queue)) {
ev = list_struct(win->queue.prev, struct pm_event);
del_from_list(ev);
}
if (list_empty(win->queue)) {
del_from_list(win);
win->in = 0;
}
}
if (list_empty(pm_event_windows)) {
int rd;
debug_call(("M: pm_handler: read"));
EINTRLOOP(rd, read(pm_pipe[0], &c, 1));
if (rd != 1) {
if (rd < 0)
fatal_exit("error on pm pipe: %s", strerror(errno));
else
fatal_exit("eof on pm pipe");
}
}
debug_call(("M: pm_handler: pm_unlock"));
flush_font = pm_flush_font_cache;
pm_flush_font_cache = 0;
#ifdef WIN
if (win) {
if (win->new_dc && !win->old_dc) {
pm_release_cached_objects(win);
win->old_dc = win->dc;
win->dc = win->new_dc;
win->new_dc = NULL;
}
}
#endif
pm_unlock();
flush_bitmaps(flush_font, 0, 0);
debug_call(("M: pm_handler: pm_unlocked: %p", ev));
if (!ev) return;
debug_call(("M: pm_handler: event: %d", ev->type));
switch (ev->type) {
struct rect r;
case E_KEY:
if (win->dev->keyboard_handler)
win->dev->keyboard_handler(win->dev, ev->x1, ev->y1);
break;
case E_MOUSE:
if (win->dev->mouse_handler)
win->dev->mouse_handler(win->dev, ev->x1, ev->y1, ev->x2);
break;
case E_REDRAW:
if (win->dev->redraw_handler) {
r.x1 = ev->x1; r.y1 = ev->y1;
r.x2 = ev->x2; r.y2 = ev->y2;
win->dev->redraw_handler(win->dev, &r);
}
break;
case E_RESIZE:
win->dev->size.x2 = ev->x2;
win->dev->size.y2 = ev->y2;
if (win->dev->resize_handler) {
win->dev->resize_handler(win->dev);
}
}
debug_call(("M: pm_handler: free ev"));
free(ev);
debug_call(("M: pm_handler: done"));
}
static int pm_bitmap_count = 0;
static unsigned char *pm_get_af_unix_name(void)
{
return cast_uchar "";
}
#ifdef PM_SPAWN_SUBPROC
static int pm_sin, pm_sout, pm_serr, pm_ip[2], pm_op[2], pm_ep[2];
static int pm_cons_ok = 0;
static void pm_setup_console(int undo)
{
int rs;
if (pm_cons_ok != undo) return;
if (pm_cons_ok) goto fail9;
setmode(1, O_BINARY);
setmode(2, O_BINARY);
pm_sin = c_dup(0);
if (pm_sin < 0) goto fail;
pm_sout = c_dup(1);
if (pm_sout < 0) goto fail1;
pm_serr = c_dup(2);
if (pm_serr < 0) goto fail2;
if (c_pipe(pm_ip)) goto fail3;
if (c_pipe(pm_op)) goto fail4;
if (c_pipe(pm_ep)) goto fail5;
EINTRLOOP(rs, dup2(pm_ip[0], 0));
if (rs != 0) goto fail6;
EINTRLOOP(rs, dup2(pm_op[1], 1));
if (rs != 1) goto fail7;
EINTRLOOP(rs, dup2(pm_ep[1], 2));
if (rs != 2) goto fail8;
EINTRLOOP(rs, close(pm_ip[0]));
EINTRLOOP(rs, close(pm_op[1]));
EINTRLOOP(rs, close(pm_ep[1]));
pm_cons_ok = 1;
return;
fail9:
EINTRLOOP(rs, dup2(pm_serr, 2));
fail8:
EINTRLOOP(rs, dup2(pm_sout, 1));
fail7:
EINTRLOOP(rs, dup2(pm_sin, 0));
fail6:
EINTRLOOP(rs, close(pm_ep[0]));
EINTRLOOP(rs, close(pm_ep[1]));
fail5:
EINTRLOOP(rs, close(pm_op[0]));
EINTRLOOP(rs, close(pm_op[1]));
fail4:
EINTRLOOP(rs, close(pm_ip[0]));
EINTRLOOP(rs, close(pm_ip[1]));
fail3:
EINTRLOOP(rs, close(pm_serr));
fail2:
EINTRLOOP(rs, close(pm_sout));
fail1:
EINTRLOOP(rs, close(pm_sin));
fail: pm_cons_ok = 0;
}
static int pm_do_console_step(int slp)
{
#define CONS_BUF 20
int did_something = 0;
unsigned char buffer[CONS_BUF];
fd_set s;
struct timeval tv = { 0, 0 };
int rs;
int m = pm_op[0] > pm_ep[0] ? pm_op[0] : pm_ep[0];
int r, w;
if (pm_sin > m) m = pm_sin;
m++;
if (!pm_cons_ok) return -1;
FD_ZERO(&s);
/*FD_SET(pm_sin, &s);*/
if (m > (int)FD_SETSIZE) {
fatal_exit("too big handle %d", m - 1);
}
FD_SET(pm_op[0], &s);
FD_SET(pm_ep[0], &s);
EINTRLOOP(rs, select(m, &s, NULL, NULL, slp ? NULL : &tv));
if (rs <= 0) return -1;
#define SEL_CHK(ih, oh) \
if (FD_ISSET(ih, &s)) { \
EINTRLOOP(r, read(ih, buffer, CONS_BUF)); \
if (r <= 0) return -1; \
do { \
if ((w = hard_write(oh, buffer, r)) <= 0) return -1;\
did_something = 1; \
r -= w; \
} while (r > 0); \
}
block_signals(0, 0);
/*SEL_CHK(pm_sin, pm_ip[1]);*/
SEL_CHK(pm_op[0], pm_sout);
SEL_CHK(pm_ep[0], pm_serr);
unblock_signals();
return did_something;
#undef SEL_CHK
}
static void pm_do_console(void)
{
while (pm_do_console_step(1) >= 0)
;
}
static void pm_sigcld(void *p)
{
int st;
pid_t w;
EINTRLOOP(w, wait(&st));
while (pm_do_console_step(0) > 0)
;
if (w > 0 && WIFEXITED(st)) exit(WEXITSTATUS(st));
else exit(RET_FATAL);
}
static unsigned char *pm_spawn_subproc(void)
{
char **arg;
int rs;
pid_t pm_child_pid;
if ((unsigned)g_argc > MAXINT / sizeof(char *) - 1) overalloc();
arg = mem_alloc((g_argc + 1) * sizeof(char *));
memcpy(arg, g_argv, g_argc * sizeof(char *));
arg[g_argc] = NULL;
pm_child_pid = -1;
install_signal_handler(SIGCHLD, pm_sigcld, NULL, 1);
pm_setup_console(0);
pm_child_pid = spawnvp(P_PM, path_to_exe, arg);
mem_free(arg);
if (pm_child_pid < 0) {
set_sigcld();
pm_setup_console(1);
return cast_uchar "Unable to spawn subprocess.\n";
}
pm_do_console();
pm_setup_console(1);
while (1)
EINTRLOOP(rs, select(1, NULL, NULL, NULL, NULL));
return cast_uchar "Not reached.\n";
}
#endif
static void pm_set_palette(void)
{
#ifdef OS2
HPAL p;
int i;
struct pm_window *win;
if (pm_use_palette) {
p = hpal;
pmshell_driver.get_color = get_color_fn(pmshell_driver.depth);
} else {
p = NULLHANDLE;
pmshell_driver.get_color = pm_get_color;
}
if (GpiSelectPalette(hps_hidden, p) == (HPAL)PAL_ERROR)
error("GpiSelectPalette failed: %lx", WinGetLastError(hab));
if (!pm_use_palette) {
if (!GpiCreateLogColorTable(hps_hidden, LCOL_RESET | LCOL_PURECOLOR, LCOLF_RGB, 0, 0, NULL))
error("GpiCreateLogColorTable failed: %lx", WinGetLastError(hab));
}
for (i = 0; i < WIN_HASH; i++) for (win = pm_windows[i]; win; win = win->nxt) {
if (GpiSelectPalette(win->ps, p) == (HPAL)PAL_ERROR)
error("GpiSelectPalette failed: %lx", WinGetLastError(hab));
if (!pm_use_palette) {
if (!GpiCreateLogColorTable(win->ps, LCOL_RESET | LCOL_PURECOLOR, LCOLF_RGB, 0, 0, NULL))
error("GpiCreateLogColorTable failed: %lx", WinGetLastError(hab));
}
if (pm_use_palette) {
ULONG pcclr;
LONG nc;
nc = WinRealizePalette(win->hc, win->ps, &pcclr);
if (nc == PAL_ERROR)
error("WinRealizePalette failed: %lx", WinGetLastError(hab));
}
}
#endif
#ifdef WIN
pmshell_driver.get_color = pm_get_color;
#endif
}
static unsigned char *pm_init_driver(unsigned char *param, unsigned char *display)
{
unsigned char *s;
int rs;
#ifdef OS2
if (os2_pib) {
if (os2_pib->pib_ultype != 3 &&
os2_pib->pib_ultype != 4) {
#ifdef PM_SPAWN_SUBPROC
s = pm_spawn_subproc();
goto r0;
#else
os2_pib->pib_ultype = 3;
#endif
}
}
if ((hab = WinInitialize(0)) == 0) {
s = cast_uchar "WinInitialize failed.\n";
goto r0;
}
#endif
#ifdef WIN
module_handle = GetModuleHandleA(NULL);
if (!module_handle) {
s = cast_uchar "Unable to get module handle.\n";
goto r0;
}
#endif
if (c_pipe(pm_pipe)) {
s = cast_uchar "Could not create pipe.\n";
goto r1;
}
set_nonblock(pm_pipe[1]);
memset(pm_windows, 0, sizeof(struct pm_window *) * WIN_HASH);
if (pm_lock_init()) {
s = cast_uchar "Could not create mutext.\n";
goto r2;
}
pm_thread_shutdown = 0;
#ifdef OS2
if (_beginthread(pm_dispatcher, NULL, 65536, NULL) == -1) {
s = cast_uchar "Could not start thread.\n";
goto r3;
}
#endif
#ifdef WIN
if (pthread_create(&pthread_handle, NULL, pm_dispatcher_win32, NULL)) {
s = cast_uchar "Could not start thread.\n";
goto r3;
}
#endif
pm_event_wait();
if (pm_status) {
s = pm_status;
goto r3;
}
#ifdef OS2
pmshell_driver.flags &= ~GD_SWITCH_PALETTE;
pmshell_driver.depth = 0xc3;
{
unsigned i;
LONG formats[16];
memset(formats, 0, sizeof formats);
if (GpiQueryDeviceBitmapFormats(hps_hidden, array_elements(formats), formats) == FALSE) goto db;
for (i = 0; array_elements(formats) - i > 1; i += 2) {
/*if (formats[i] || formats[i + 1]) fprintf(stderr, "format[%d]: %ld, %ld\n", i, formats[i], formats[i + 1]);*/
if (formats[i] == 1) switch (formats[i+1]) {
case 4:
/* 216 colors with rounding makes text look better, but images worse */
#if 0
pmshell_driver.depth = 0x21;
pmshell_driver.depth |= 0x300;
goto db;
#endif
case 8:
pmshell_driver.depth = 0x41;
pmshell_driver.depth |= 0x300;
goto db;
case 15:
pmshell_driver.depth |= 15 * 1024;
goto db;
case 16:
pmshell_driver.depth |= 16 * 1024;
goto db;
case 24:
goto db;
}
}
}
db:
{
unsigned i, pm_colors;
unsigned bit_count = (pmshell_driver.depth >> 3) & 0x1f;
if (bit_count <= 4)
pm_colors = !(pmshell_driver.depth & 0x300) ? 16 : 8;
else
pm_colors = !(pmshell_driver.depth & 0x300) ? 256 : 216;
pm_bitmapinfo = pm_alloc(sizeof(BITMAPINFOHEADER) + (bit_count <= 8 ? 256 : 1) * sizeof(RGB), PM_ALLOC_ZERO);
pm_bitmapinfo->cbFix = sizeof(BITMAPINFOHEADER);
pm_bitmapinfo->cPlanes = 1;
pm_bitmapinfo->cBitCount = bit_count <= 8 ? 8 : bit_count;
hpal = NULLHANDLE;
if (bit_count <= 8) {
LONG dev_planes, dev_bitcount, dev_additional;
ULONG pm_palette[256];
HDC hdc;
memset(pm_palette, 0, sizeof pm_palette);
for (i = 0; i < 256; i++) {
unsigned rgb[3];
q_palette(pm_colors, i, 255, rgb);
pm_bitmapinfo->argbColor[i].bBlue = rgb[2];
pm_bitmapinfo->argbColor[i].bGreen = rgb[1];
pm_bitmapinfo->argbColor[i].bRed = rgb[0];
pm_palette[i] = (rgb[0] << 16) | (rgb[1] << 8) | rgb[2];
}
hdc = GpiQueryDevice(hps_hidden);
if (hdc == HDC_ERROR) {
error("GpiQueryDevice failed: %lx", WinGetLastError(hab));
goto skip_palette;
}
if (!DevQueryCaps(hdc, CAPS_COLOR_PLANES, 1, &dev_planes) ||
!DevQueryCaps(hdc, CAPS_COLOR_BITCOUNT, 1, &dev_bitcount) ||
!DevQueryCaps(hdc, CAPS_ADDITIONAL_GRAPHICS, 1, &dev_additional)) {
error("GpiQueryCaps failed: %lx", WinGetLastError(hab));
goto skip_palette;
}
/*fprintf(stderr, "caps: %lx %lx %lx\n", dev_planes, dev_bitcount, dev_additional);*/
if (dev_planes != 1)
goto skip_palette;
if (dev_bitcount < 8)
goto skip_palette;
if (!(dev_additional & CAPS_PALETTE_MANAGER))
goto skip_palette;
hpal = GpiCreatePalette(hab, LCOL_PURECOLOR /*| LCOL_OVERRIDE_DEFAULT_COLORS*/, LCOLF_CONSECRGB, pm_colors, pm_palette);
if (hpal == GPI_ERROR) {
error("GpiCreatePalette failed: %lx", WinGetLastError(hab));
hpal = NULLHANDLE;
}
if (hpal != NULLHANDLE) {
if (GpiSelectPalette(hps_hidden, hpal) == (HPAL)PAL_ERROR) {
error("GpiSelectPalette failed: %lx", WinGetLastError(hab));
if (!GpiDeletePalette(hpal))
error("GpiDeletePalette failed: %lx", WinGetLastError(hab));
hpal = NULLHANDLE;
}
if (GpiSelectPalette(hps_hidden, NULLHANDLE) == (HPAL)PAL_ERROR)
error("GpiSelectPalette failed: %lx", WinGetLastError(hab));
pmshell_driver.flags |= GD_SWITCH_PALETTE;
}
}
skip_palette:
pm_set_palette();
}
{
SIZEL sizl = { 0, 0 };
PSZ data[4] = { "DISPLAY", NULL, NULL, NULL };
hdc_mem = DevOpenDC(hab, OD_MEMORY, "*", 4L, (PDEVOPENDATA)data, NULLHANDLE);
if (!hdc_mem) {
s = cast_uchar "Unable to create memory device context.\n";
goto r4;
}
hps_mem = GpiCreatePS(hab, hdc_mem, &sizl, GPIA_ASSOC | PU_PELS | GPIT_MICRO);
if (!hps_mem) {
s = cast_uchar "Unable to create memory presentation space.\n";
goto r5;
}
}
icon = WinLoadPointer(HWND_DESKTOP, 0, 1);
#endif
#ifdef WIN
{
int bpp;
screen_dc = GetDC(NULL);
if (!screen_dc) {
s = cast_uchar "Unable to get screen device context.\n";
goto r4;
}
bpp = GetDeviceCaps(screen_dc, BITSPIXEL);
if (bpp < 24)
pmshell_driver.depth = 0x7a;
else
pmshell_driver.depth = 0xc3;
pm_bitmapinfo = pm_alloc(sizeof(BITMAPINFO), PM_ALLOC_ZERO);
pm_bitmapinfo->bmiHeader.biSize = sizeof(pm_bitmapinfo->bmiHeader);
pm_bitmapinfo->bmiHeader.biPlanes = 1;
pm_bitmapinfo->bmiHeader.biBitCount = bpp < 24 ? 16 : 24;
pm_bitmapinfo->bmiHeader.biCompression = BI_RGB;
}
icon_big = LoadImage(module_handle, MAKEINTRESOURCE(1), IMAGE_ICON, 32, 32, LR_DEFAULTCOLOR);
icon_small = LoadImage(module_handle, MAKEINTRESOURCE(1), IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR);
pm_set_palette();
#endif
icon_set = 0;
set_handlers(pm_pipe[0], pm_handler, NULL, NULL);
return NULL;
#ifdef OS2
r5:
if (DevCloseDC(hdc_mem) == DEV_ERROR)
error("DevCloseDC failed: %lx", WinGetLastError(hab));
#endif
r4:
pm_free(pm_bitmapinfo);
pm_send_msg(MSG_SHUTDOWN_THREAD, NULL, NULL);
r3:
PM_UNFLUSH();
pm_lock_close();
r2:
EINTRLOOP(rs, close(pm_pipe[0]));
EINTRLOOP(rs, close(pm_pipe[1]));
r1:
#ifdef OS2
if (!WinTerminate(hab))
error("WinTerminate failed: %lx", WinGetLastError(hab));
#endif
r0:
return stracpy(s);
}
static void pm_shutdown_driver(void)
{
int rs;
pm_send_msg(MSG_SHUTDOWN_THREAD, NULL, NULL);
#ifdef OS2
if (icon != NULLHANDLE)
if (!WinDestroyPointer(icon))
error("WinDestroyPointer failed: %lx", WinGetLastError(hab));
if (hpal != NULLHANDLE) {
if (!GpiDeletePalette(hpal))
error("GpiDeletePalette failed: %lx", WinGetLastError(hab));
}
if (!GpiDestroyPS(hps_mem))
error("GpiDestroyPS failed: %lx", WinGetLastError(hab));
if (DevCloseDC(hdc_mem) == DEV_ERROR)
error("DevCloseDC failed: %lx", WinGetLastError(hab));
#endif
#ifdef WIN
if (icon_small != NULL)
if (!DestroyIcon(icon_small))
error("DestroIcon failed: %u", (unsigned)GetLastError());
if (icon_big != NULL)
if (!DestroyIcon(icon_big))
error("DestroIcon failed: %u", (unsigned)GetLastError());
if (!ReleaseDC(NULL, screen_dc))
error("ReleaseDC failed: %u", (unsigned)GetLastError());
#endif
pm_free(pm_bitmapinfo);
PM_UNFLUSH();
pm_lock_close();
close_socket(&pm_pipe[0]);
EINTRLOOP(rs, close(pm_pipe[1]));
#ifdef OS2
if (!WinTerminate(hab))
error("WinTerminate failed: %lx", WinGetLastError(hab));
#endif
if (pm_bitmap_count) internal_error("pm_shutdown_driver: %d bitmaps leaked", pm_bitmap_count);
}
#ifdef OS2
static HPOINTER pmshell_create_icon(void)
{
unsigned char *icon_data, *icon_data_2;
int icon_w, icon_h;
ssize_t icon_skip;
int size;
int i, j;
HBITMAP hbm;
HPOINTER hicon;
HPAL prev_pal;
const int border = 4;
get_links_icon(&icon_data, &icon_w, &icon_h, &icon_skip, 4);
size = (int)(icon_h * icon_skip);
icon_data_2 = pm_alloc(size * 2, 0);
for (i = 0; i < icon_h; i++)
memcpy(icon_data_2 + i * icon_skip, icon_data + (icon_h - 1 - i) * icon_skip, icon_skip);
mem_free(icon_data);
for (i = 0; i < size; i++) {
icon_data_2[i + size] = 0;
}
#define set_trans(x, y) memset(icon_data_2 + ((y) + icon_h) * icon_skip + (x) * (pmshell_driver.depth & 7), 0xff, pmshell_driver.depth & 7);
for (j = 0; j < border; j++) for (i = 0; i < icon_w; i++) set_trans(i, j);
for (j = icon_h - border; j < icon_h; j++) for (i = 0; i < icon_w; i++) set_trans(i, j);
for (j = 0; j < icon_h; j++) for (i = 0; i < border; i++) set_trans(i, j);
for (j = 0; j < icon_h; j++) for (i = icon_w - border; i < icon_w; i++) set_trans(i, j);
#undef set_trans
pm_bitmapinfo->cx = icon_w;
pm_bitmapinfo->cy = icon_h * 2;
if ((prev_pal = GpiSelectPalette(hps_hidden, NULLHANDLE)) == (HPAL)PAL_ERROR)
error("GpiSelectPalette failed: %lx", WinGetLastError(hab));
hbm = GpiCreateBitmap(hps_hidden, (PBITMAPINFOHEADER2)pm_bitmapinfo, CBM_INIT, icon_data_2, (PBITMAPINFO2)pm_bitmapinfo);
if (prev_pal != (HPAL)PAL_ERROR) {
if (GpiSelectPalette(hps_hidden, prev_pal) == (HPAL)PAL_ERROR)
error("GpiSelectPalette failed: %lx", WinGetLastError(hab));
}
pm_free(icon_data_2);
if (hbm == GPI_ERROR)
return NULLHANDLE;
hicon = WinCreatePointer(HWND_DESKTOP, hbm, FALSE, 0, 0);
if (!hicon)
error("WinCreatePointer failed: %lx", WinGetLastError(hab));
if (!GpiDeleteBitmap(hbm))
error("GpiDeleteBitmap failed: %lx", WinGetLastError(hab));
return hicon;
}
#endif
#ifdef WIN
static HICON win32_create_icon(int x, int y)
{
unsigned char *icon_data, *zero_data;
int icon_w, icon_h;
ssize_t icon_skip;
int left, right, top, bottom;
int i, j;
HBITMAP hbm_icon;
HBITMAP hbm_mask;
ICONINFO ic;
HICON hicon = NULL;
HDC memory_dc;
HGDIOBJ prev_obj;
get_links_icon(&icon_data, &icon_w, &icon_h, &icon_skip, 4);
zero_data = pm_alloc(icon_h * icon_skip, 0);
memcpy(zero_data, icon_data, icon_h * icon_skip);
mem_free(icon_data);
icon_data = zero_data;
zero_data = pm_alloc(icon_h * icon_skip, PM_ALLOC_ZERO);
memory_dc = CreateCompatibleDC(NULL);
if (!memory_dc) {
error("CreateCompatibleDC failed: %u", (unsigned)GetLastError());
goto ret1;
}
hbm_icon = CreateCompatibleBitmap(screen_dc, x, y);
if (!hbm_icon) {
error("CreateBitmap failed: %u", (unsigned)GetLastError());
goto ret2;
}
prev_obj = SelectObject(memory_dc, hbm_icon);
if (!prev_obj) {
error("SelectObject failed: %u", (unsigned)GetLastError());
goto ret3;
}
pm_bitmapinfo->bmiHeader.biWidth = icon_w;
pm_bitmapinfo->bmiHeader.biHeight = -icon_h;
if (!StretchDIBits(memory_dc, 0, 0, x, y, 0, 0, icon_w, icon_h, icon_data, pm_bitmapinfo, DIB_RGB_COLORS, SRCCOPY)) {
error("StretchDIBits failed: %u", (unsigned)GetLastError());
prev_obj = SelectObject(memory_dc, prev_obj);
if (!prev_obj) {
error("SelectObject failed: %u", (unsigned)GetLastError());
goto ret3;
}
goto ret3;
}
prev_obj = SelectObject(memory_dc, prev_obj);
if (!prev_obj) {
error("SelectObject failed: %u", (unsigned)GetLastError());
goto ret3;
}
hbm_mask = CreateBitmap(x, y, 1, 1, zero_data);
if (!hbm_mask) {
error("CreateCompatibleBitmap failed: %u", (unsigned)GetLastError());
goto ret3;
}
prev_obj = SelectObject(memory_dc, hbm_mask);
if (!prev_obj) {
error("SelectObject failed %u", (unsigned)GetLastError());
goto ret4;
}
if (x == 16 && y == 16) {
top = bottom = left = right = 1;
if (is_winnt())
top = 2;
} else if (x == 32 && y == 32) {
top = bottom = left = right = 3;
} else {
top = bottom = left = right = 0;
}
for (j = 0; j < top; j++) for (i = 0; i < x; i++) SetPixel(memory_dc, i, j, 0xffffff);
for (j = y - bottom; j < y; j++) for (i = 0; i < x; i++) SetPixel(memory_dc, i, j, 0xffffff);
for (j = 0; j < y; j++) for (i = 0; i < left; i++) SetPixel(memory_dc, i, j, 0xffffff);
for (j = 0; j < y; j++) for (i = x - left; i < x; i++) SetPixel(memory_dc, i, j, 0xffffff);
prev_obj = SelectObject(memory_dc, prev_obj);
if (!prev_obj) {
error("SelectObject failed %u", (unsigned)GetLastError());
goto ret4;
}
memset(&ic, 0, sizeof ic);
ic.fIcon = TRUE;
ic.hbmMask = hbm_mask;
ic.hbmColor = hbm_icon;
hicon = CreateIconIndirect(&ic);
if (!hicon)
error("CreateIconIndirect failed %u", (unsigned)GetLastError());
ret4:
if (!DeleteBitmap(hbm_mask))
error("DeleteBitmap failed %u", (unsigned)GetLastError());
ret3:
if (!DeleteBitmap(hbm_icon))
error("DeleteBitmap failed %u", (unsigned)GetLastError());
ret2:
if (!DeleteDC(memory_dc))
error("DeleteDC failed %u", (unsigned)GetLastError());
ret1:
pm_free(icon_data);
pm_free(zero_data);
return hicon;
}
#endif
static struct graphics_device *pm_init_device(void)
{
RECTL wr;
struct graphics_device *dev;
struct pm_window *win;
if (!icon_set) {
#ifdef OS2
if (icon == NULLHANDLE) icon = pmshell_create_icon();
#endif
#ifdef WIN
if (!icon_big) icon_big = win32_create_icon(32, 32);
if (!icon_small) icon_small = win32_create_icon(16, 16);
#endif
}
icon_set = 1;
win = mem_alloc(sizeof(struct pm_window));
win->lastpos = -1L;
win->button = 0;
init_list(win->queue);
win->in = 0;
win->minimized = 0;
#ifdef WIN
win->dc = NULL;
win->old_dc = NULL;
win->new_dc = NULL;
win->pen_orig = NULL;
win->pen_cache = NULL;
win->pen_cache_color = -1;
win->brush_cache = NULL;
win->brush_cache_color = -1;
win->drawing_cookie = (tcount)-1;
win->timer_active = 0;
#endif
debug_call(("M: pm_init_device: pm_send_msg"));
pm_send_msg(MSG_CREATE_WINDOW, win, NULL);
debug_call(("M: pm_init_device: pm_send_msg done"));
#ifdef OS2
if (win->h == NULLHANDLE) {
goto r1;
}
debug_call(("M: pm_init_device: WinGetPS"));
if ((win->ps = WinGetPS(win->hc)) == NULLHANDLE) {
goto r2;
}
if ((win->rgn = GpiCreateRegion(win->ps, 0, NULL)) == RGN_ERROR) {
goto r3;
}
#endif
#ifdef WIN
if (win->hc == NULL) {
goto r1;
}
if ((win->rgn = CreateRectRgn(0, 0, 0, 0)) == NULL) {
goto r3;
}
#endif
dev = mem_calloc(sizeof(struct graphics_device));
dev->driver_data = win;
win->dev = dev;
#ifdef OS2
debug_call(("M: pm_init_device: WinQueryWindowRect"));
if (!WinQueryWindowRect(win->hc, &wr)) {
memset(&wr, 0, sizeof wr);
}
#endif
#ifdef WIN
if (!GetClientRect(win->hc, &wr)) {
memset(&wr, 0, sizeof wr);
}
wr.yTop = wr.yBottom;
#endif
win->x = dev->size.x2 = wr.xRight;
win->y = dev->size.y2 = wr.yTop;
dev->clip.x1 = dev->clip.y1 = 0;
dev->clip.x2 = dev->size.x2;
dev->clip.y2 = dev->size.y2;
debug_call(("M: pm_init_device: pm_hash_window"));
pm_hash_window(win);
debug_call(("M: pm_init_device: pm_unlock"));
pm_unlock();
pm_set_palette();
debug_call(("M: pm_init_device: return"));
return dev;
r3:
#ifdef OS2
if (!WinReleasePS(win->ps))
error("WinReleasePS failed: %lx", WinGetLastError(hab));
r2:
#endif
pm_unlock();
pm_send_msg(MSG_DELETE_WINDOW, win, NULL);
r1:
if (win->in) del_from_list(win);
pm_unlock();
mem_free(win);
return NULL;
}
static void pm_shutdown_device(struct graphics_device *dev)
{
struct pm_window *win = pm_win(dev);
#ifdef OS2
debug_call(("M: pm_shutdown_device: WinReleasePS"));
if (!GpiDestroyRegion(win->ps, win->rgn))
error("GpiDestroyRegion failed: %lx", WinGetLastError(hab));
if (!WinReleasePS(win->ps))
error("WinReleasePS failed: %lx", WinGetLastError(hab));
#endif
#ifdef WIN
if (!DeleteObject(win->rgn))
error("DeleteObject failed for region %u", (unsigned)GetLastError());
pm_release_cached_objects(win);
#endif
debug_call(("M: pm_shutdown_device: pm_send_msg"));
pm_send_msg(MSG_DELETE_WINDOW, win, NULL);
debug_call(("M: pm_shutdown_device: pm_unhash_window"));
pm_unhash_window(win);
if (win->in) del_from_list(win);
debug_call(("M: pm_shutdown_device: pm_unlock"));
pm_unlock();
debug_call(("M: pm_shutdown_device: free"));
while (!list_empty(win->queue)) {
struct pm_event *ev = list_struct(win->queue.next, struct pm_event);
del_from_list(ev);
free(ev);
}
mem_free(win);
mem_free(dev);
debug_call(("M: pm_shutdown_device: return"));
}
#define MAX_TITLE_SIZE 512
static void pm_set_window_title(struct graphics_device *dev, unsigned char *title)
{
unsigned char *text;
debug_call(("M: pm_set_window_title: start"));
#ifdef WIN
if (unicode_supported && unicode_title_supported) {
int l = 0;
text = init_str();
while (*title && l < MAX_TITLE_SIZE * 2) {
unsigned u;
GET_UTF_8(title, u);
if (u > 0 && u <= 0xffff) {
add_chr_to_str(&text, &l, u & 0xff);
add_chr_to_str(&text, &l, u >> 8);
}
}
add_chr_to_str(&text, &l, 0);
} else
#endif
{
text = convert(utf8_table, pm_cp, title, NULL);
clr_white(text);
if (strlen(cast_const_char text) > MAX_TITLE_SIZE) text[MAX_TITLE_SIZE] = 0;
}
debug_call(("M: pm_set_window_title: pm_send_msg"));
pm_send_msg(MSG_SET_WINDOW_TITLE, pm_win(dev), text);
debug_call(("M: pm_set_window_title: pm_unlock"));
pm_unlock();
/*SendMessage(pm_win(dev)->hc, WM_SETTEXT, NULL, text);*/
debug_call(("M: pm_set_window_title: mem_free"));
mem_free(text);
debug_call(("M: pm_set_window_title: return"));
}
static int pm_get_empty_bitmap(struct bitmap *bmp)
{
unsigned size;
debug_call(("M: get_empty_bitmap (%dx%d)", bmp->x, bmp->y));
if ((unsigned)bmp->x > (unsigned)MAXINT / (pmshell_driver.depth & 7) - 4) {
over:
bmp->data = NULL;
bmp->flags = NULL;
return -1;
}
bmp->skip = (bmp->x * (pmshell_driver.depth & 7) + 3) & ~3;
size = (unsigned)bmp->skip * (unsigned)bmp->y;
if (bmp->skip && size / (unsigned)bmp->skip != (unsigned)bmp->y) goto over;
if (size > MAXINT) goto over;
bmp->data = pm_alloc(size, PM_ALLOC_MAYFAIL);
if (!bmp->data) goto over;
bmp->data = (unsigned char *)bmp->data + size - bmp->skip;
bmp->skip = -bmp->skip;
debug_call(("M: get_empty_bitmap done"));
return 0;
}
static inline unsigned char *bmp_base_pointer(struct bitmap *bmp)
{
return (unsigned char *)bmp->data + bmp->skip * (bmp->y - 1);
}
static void pm_register_bitmap(struct bitmap *bmp)
{
#ifdef OS2
HBITMAP hbm;
unsigned char *pointer;
#endif
debug_call(("M: register_bitmap (%dx%d)", bmp->x, bmp->y));
pm_bitmap_count++;
#ifdef OS2
if (!bmp->data) {
bmp->flags = (void *)GPI_ERROR;
return;
}
pointer = bmp_base_pointer(bmp);
again:
if (bmp->x > OS2_MAX_BITMAP_SIZE || bmp->y > OS2_MAX_BITMAP_SIZE) {
hbm = GPI_ERROR;
} else {
pm_bitmapinfo->cx = bmp->x;
pm_bitmapinfo->cy = bmp->y;
hbm = GpiCreateBitmap(hps_hidden, (PBITMAPINFOHEADER2)pm_bitmapinfo, CBM_INIT, pointer, (PBITMAPINFO2)pm_bitmapinfo);
if (hbm == GPI_ERROR) {
if (out_of_memory(MF_GPI, NULL, 0))
goto again;
}
}
bmp->flags = (void *)hbm;
if (hbm != GPI_ERROR) {
mem_free(pointer);
bmp->data = NULL;
}
debug_call(("M: register_bitmap done"));
#endif
}
static void *pm_prepare_strip(struct bitmap *bmp, int top, int lines)
{
#ifdef OS2
debug_call(("M: prepare_strip (%dx%d)", bmp->x, bmp->y));
if (bmp->flags == (void *)GPI_ERROR) {
if (bmp->data)
goto return_data;
over:
bmp->data = NULL;
return NULL;
}
if (bmp->data)
internal_error("pm_prepare_strip: bmp->data should not be set here");
if (-bmp->skip && (unsigned)-bmp->skip * (unsigned)lines / (unsigned)-bmp->skip != (unsigned)lines) goto over;
if ((unsigned)-bmp->skip * (unsigned)lines > MAXINT) goto over;
bmp->data = pm_alloc(-bmp->skip * lines, PM_ALLOC_MAYFAIL);
if (!bmp->data) goto over;
debug_call(("M: prepare_strip done"));
return (unsigned char *)bmp->data - bmp->skip * (lines - 1);
return_data:
#endif
return ((unsigned char *)bmp->data) + bmp->skip * top;
}
static void pm_commit_strip(struct bitmap *bmp, int top, int lines)
{
#ifdef OS2
LONG r;
HBITMAP old;
HBITMAP new = (HBITMAP)bmp->flags;
debug_call(("M: commit_strip (%dx%d)", bmp->x, bmp->y));
if (new == GPI_ERROR || !bmp->data)
return;
debug_call(("M: commit_strip done"));
old = GpiSetBitmap(hps_mem, new);
if (old == HBM_ERROR)
goto ret;
pm_bitmapinfo->cx = bmp->x;
pm_bitmapinfo->cy = bmp->y;
again:
r = GpiSetBitmapBits(hps_mem, bmp->y - top - lines, lines, bmp->data, (PBITMAPINFO2)pm_bitmapinfo);
if (r == GPI_ALTERROR) {
if (out_of_memory(MF_GPI, NULL, 0))
goto again;
}
GpiSetBitmap(hps_mem, old);
ret:
pm_free(bmp->data);
bmp->data = NULL;
debug_call(("M: commit_strip done"));
#endif
}
static void pm_unregister_bitmap(struct bitmap *bmp)
{
debug_call(("M: unregister_bitmap (%dx%d)", bmp->x, bmp->y));
pm_bitmap_count--;
#ifdef OS2
if ((HBITMAP)bmp->flags != GPI_ERROR) {
if (bmp->data)
internal_error("pm_unregister_bitmap: bmp->data should not be set here");
if (!GpiDeleteBitmap((HBITMAP)bmp->flags))
error("GpiDeleteBitmap failed: %lx", WinGetLastError(hab));
} else
#endif
if (bmp->data) {
pm_free(bmp_base_pointer(bmp));
}
debug_call(("M: unregister_bitmap done"));
}
static void pm_draw_bitmap(struct graphics_device *dev, struct bitmap *bmp, int x, int y)
{
POINTL p;
RECTL r;
debug_call(("M: draw_bitmap (%dx%d -> %x,%x)", bmp->x, bmp->y, x, y));
CLIP_DRAW_BITMAP
PM_START_DRAW();
r.xLeft = x < dev->clip.x1 ? dev->clip.x1 - x : 0;
r.xRight = x + bmp->x > dev->clip.x2 ? dev->clip.x2 - x : bmp->x;
r.yTop = bmp->y - (y < dev->clip.y1 ? dev->clip.y1 - y : 0);
r.yBottom = y + bmp->y > dev->clip.y2 ? bmp->y - dev->clip.y2 + y : 0;
p.x = x + r.xLeft;
p.y = pm_win(dev)->y - y - bmp->y + r.yBottom;
#ifdef OS2
if ((HBITMAP)bmp->flags != GPI_ERROR) {
WinDrawBitmap(pm_win(dev)->ps, (HBITMAP)bmp->flags, &r, &p, 0, 1, DBM_NORMAL);
} else if (!bmp->data) {
return;
} else {
POINTL pp[4];
if (bmp->x > OS2_MAX_LINE_SIZE) {
int i;
unsigned char *data_ptr = (unsigned char *)bmp->data + bmp->skip * (bmp->y - r.yTop) + r.xLeft * (pmshell_driver.depth & 7);
for (i = r.yTop - r.yBottom - 1; i >= 0; i--) {
pm_bitmapinfo->cx = r.xRight - r.xLeft;
pm_bitmapinfo->cy = 1;
pp[0].x = p.x;
pp[0].y = p.y + i;
pp[1].x = p.x + (r.xRight - r.xLeft) - 1;
pp[1].y = p.y + i;
pp[2].x = 0;
pp[2].y = 0;
pp[3].x = r.xRight - r.xLeft;
pp[3].y = 1;
GpiDrawBits(pm_win(dev)->ps, data_ptr, (PBITMAPINFO2)pm_bitmapinfo, 4, pp, ROP_SRCCOPY, 0);
data_ptr += bmp->skip;
}
} else {
pm_bitmapinfo->cx = bmp->x;
pm_bitmapinfo->cy = r.yTop - r.yBottom;
pp[0].x = p.x;
pp[0].y = p.y;
pp[1].x = p.x + (r.xRight - r.xLeft) - 1;
pp[1].y = p.y + (r.yTop - r.yBottom) - 1;
pp[2].x = r.xLeft;
pp[2].y = 0;
pp[3].x = r.xRight;
pp[3].y = r.yTop - r.yBottom;
GpiDrawBits(pm_win(dev)->ps, (unsigned char *)bmp->data + bmp->skip * (bmp->y - 1 - r.yBottom), (PBITMAPINFO2)pm_bitmapinfo, 4, pp, ROP_SRCCOPY, 0);
}
}
#endif
#ifdef WIN
if (!bmp->data)
return;
pm_bitmapinfo->bmiHeader.biWidth = bmp->x;
pm_bitmapinfo->bmiHeader.biHeight = r.yTop - r.yBottom;
SetDIBitsToDevice(pm_win(dev)->dc, p.x, pm_win(dev)->y - p.y - (r.yTop - r.yBottom), r.xRight - r.xLeft, r.yTop - r.yBottom, r.xLeft, 0, 0, r.yTop - r.yBottom, (unsigned char *)bmp->data + bmp->skip * (bmp->y - 1 - r.yBottom), pm_bitmapinfo, DIB_RGB_COLORS);
#endif
debug_call(("M: draw_bitmap done"));
}
static long pm_get_color(int rgb)
{
#ifdef OS2
int c;
rgb &= 0xffffff;
c = GpiQueryNearestColor(hps_hidden, 0, rgb);
if (c == GPI_ALTERROR) return rgb;
/*fprintf(stderr, "pm_get_color: %x -> %x\n", rgb, c);*/
return c;
#endif
#ifdef WIN
COLORREF c, d;
c = ((rgb & 0xff) << 16) | (rgb & 0xff00) | ((rgb & 0xff0000) >> 16);
d = GetNearestColor(screen_dc, c);
if (d == CLR_INVALID) return c;
return d;
#endif
}
#ifdef WIN
static int win32_select_brush(struct pm_window *win, int color)
{
HBRUSH hbrush;
if (color == win->brush_cache_color)
return 0;
hbrush = CreateSolidBrush(color);
if (!hbrush) {
error("CreateSolidBrush failed %u", (unsigned)GetLastError());
return -1;
}
if (win->brush_cache != NULL)
if (!DeleteObject(win->brush_cache))
error("DeleteObject failed for previous brush %u", (unsigned)GetLastError());
win->brush_cache = hbrush;
win->brush_cache_color = color;
return 0;
}
static int win32_select_pen(struct pm_window *win, int color)
{
HPEN hpen;
HGDIOBJ orig;
if (color == win->pen_cache_color)
return 0;
hpen = CreatePen(PS_SOLID, 0, color);
if (!hpen) {
error("CreatePen failed %u", (unsigned)GetLastError());
return -1;
}
orig = SelectObject(win->dc, hpen);
if (!orig) {
error("SelectObject failed for pen %u", (unsigned)GetLastError());
if (!DeleteObject(hpen))
error("DeleteObject failed for pen %u", (unsigned)GetLastError());
return -1;
}
if (!win->pen_orig)
win->pen_orig = orig;
else if (!DeleteObject(orig))
error("DeleteObject failed for previous pen %u", (unsigned)GetLastError());
win->pen_cache = hpen;
win->pen_cache_color = color;
return 0;
}
#endif
static void pm_fill_area(struct graphics_device *dev, int x1, int y1, int x2, int y2, long color)
{
RECTL r;
debug_call(("M: fill_area (%d,%d)->(%d,%d)", x1, y1, x2, y2));
CLIP_FILL_AREA
PM_START_DRAW();
r.xLeft = x1;
r.yBottom = y2;
r.xRight = x2;
r.yTop = y1;
#ifdef OS2
r.yBottom = pm_win(dev)->y - r.yBottom;
r.yTop = pm_win(dev)->y - r.yTop;
WinFillRect(pm_win(dev)->ps, &r, color);
#endif
#ifdef WIN
if (win32_select_brush(pm_win(dev), color))
return;
if (!FillRect(pm_win(dev)->dc, &r, pm_win(dev)->brush_cache))
error("FillRect failed %u", (unsigned)GetLastError());
#endif
debug_call(("M: fill_area done"));
}
static void pm_draw_hline(struct graphics_device *dev, int x1, int y, int x2, long color)
{
#ifdef OS2
HPS ps = pm_win(dev)->ps;
POINTL p;
#endif
debug_call(("M: draw_hline (%d,%d)->(%d)", x1, y, x2));
CLIP_DRAW_HLINE
PM_START_DRAW();
#ifdef OS2
GpiSetColor(ps, color);
p.x = x1;
p.y = pm_win(dev)->y - y - 1;
GpiMove(ps, &p);
p.x = x2 - 1;
GpiLine(ps, &p);
#endif
#ifdef WIN
if (win32_select_pen(pm_win(dev), color))
return;
if (!MoveToEx(pm_win(dev)->dc, x1, y, NULL)) {
error("MoveToEx failed %u", (unsigned)GetLastError());
} else if (!LineTo(pm_win(dev)->dc, x2, y)) {
error("LineTo failed %u", (unsigned)GetLastError());
}
#endif
debug_call(("M: draw_hline done"));
}
static void pm_draw_vline(struct graphics_device *dev, int x, int y1, int y2, long color)
{
#ifdef OS2
HPS ps = pm_win(dev)->ps;
POINTL p;
#endif
debug_call(("M: draw_vline (%d,%d)->(%d)", x, y1, y2));
CLIP_DRAW_VLINE
PM_START_DRAW();
#ifdef OS2
GpiSetColor(ps, color);
p.x = x;
p.y = pm_win(dev)->y - y1 - 1;
GpiMove(ps, &p);
p.y = pm_win(dev)->y - y2;
GpiLine(ps, &p);
#endif
#ifdef WIN
if (win32_select_pen(pm_win(dev), color))
return;
if (!MoveToEx(pm_win(dev)->dc, x, y1, NULL)) {
error("MoveToEx failed %u", (unsigned)GetLastError());
} else if (!LineTo(pm_win(dev)->dc, x, y2)) {
error("LineTo failed %u", (unsigned)GetLastError());
}
#endif
debug_call(("M: draw_vline done"));
}
static void pm_scroll_redraws(struct pm_window *win, struct rect *r, int scx, int scy)
{
struct pm_event *e;
pm_cancel_event(win, E_REDRAW, &e);
if (!e) return;
if (scx >= 0) {
if (e->x2 > r->x1 && e->x2 < r->x2) {
e->x2 += scx;
if (e->x2 > r->x2) e->x2 = r->x2;
}
} else {
if (e->x1 > r->x1 && e->x1 < r->x2) {
e->x1 += scx;
if (e->x1 < r->x1) e->x1 = r->x1;
}
}
if (scy >= 0) {
if (e->y2 > r->y1 && e->y2 < r->y2) {
e->y2 += scy;
if (e->y2 > r->y2) e->y2 = r->y2;
}
} else {
if (e->y1 > r->y1 && e->y1 < r->y2) {
e->y1 += scy;
if (e->y1 < r->y1) e->y1 = r->y1;
}
}
}
static int pm_scroll(struct graphics_device *dev, struct rect_set **set, int scx, int scy)
{
struct pm_window *win = pm_win(dev);
#ifdef OS2
RGNRECT rgnrect;
#endif
#ifdef WIN32
size_t sz;
RGNDATA *rgndata;
#endif
RECTL *rects;
size_t i;
RECTL r;
PM_START_DRAW();
debug_call(("M: scroll (%d,%d)", scx, scy));
pm_lock();
r.xLeft = dev->clip.x1;
r.yBottom = dev->clip.y2;
r.xRight = dev->clip.x2;
r.yTop = dev->clip.y1;
#ifdef OS2
r.yBottom = win->y - r.yBottom;
r.yTop = win->y - r.yTop;
WinScrollWindow(win->hc, scx, -scy, &r, &r, win->rgn, NULL, 0);
memset(&rgnrect, 0, sizeof rgnrect);
rgnrect.ulDirection = RECTDIR_LFRT_TOPBOT;
if (!GpiQueryRegionRects(win->ps, win->rgn, NULL, &rgnrect, NULL)) {
error("GpiQueryRegionRects failed: %lx", WinGetLastError(hab));
goto end;
}
/*fprintf(stderr, "GpiQueryRegionRects: %lu, %lu, %lu, %lu\n", rgnrect.ircStart, rgnrect.crc, rgnrect.crcReturned, rgnrect.ulDirection);*/
if (rgnrect.crcReturned <= 1) {
if (!WinInvalidateRegion(win->hc, win->rgn, FALSE))
error("WinInvalidateRegion failed: %lx", WinGetLastError(hab));
goto end;
}
rgnrect.crc = rgnrect.crcReturned;
if (rgnrect.crcReturned > MAXINT / sizeof(RECTL)) overalloc();
rects = pm_alloc(rgnrect.crcReturned * sizeof(RECTL), 0);
if (!GpiQueryRegionRects(win->ps, win->rgn, NULL, &rgnrect, rects)) {
error("GpiQueryRegionRects failed: %lx", WinGetLastError(hab));
goto end_rects;
}
for (i = 0; i < rgnrect.crcReturned; i++) {
struct rect xr;
xr.x1 = rects[i].xLeft;
xr.x2 = rects[i].xRight;
xr.y1 = win->y - rects[i].yTop;
xr.y2 = win->y - rects[i].yBottom;
/*fprintf(stderr, "returned: %d, %d, %d, %d\n", xr.x1, xr.x2, xr.y1, xr.y2);*/
add_to_rect_set(set, &xr);
}
end_rects:
pm_free(rects);
#endif
#ifdef WIN
if (ScrollWindowEx(win->hc, scx, scy, &r, &r, win->rgn, NULL, 0) == ERROR) {
error("ScrollWindowEx failed %u", (unsigned)GetLastError());
goto end;
}
sz = GetRegionData(win->rgn, 0, NULL);
if (!sz) {
error("GetRegionData failed %u", (unsigned)GetLastError());
goto end;
}
rgndata = pm_alloc(sz, 0);
sz = GetRegionData(win->rgn, sz, rgndata);
if (!sz) {
error("GetRegionData failed %u", (unsigned)GetLastError());
goto end_rgndata;
}
if (rgndata->rdh.iType != RDH_RECTANGLES || rgndata->rdh.nCount <= 1) {
if (!InvalidateRgn(win->hc, win->rgn, FALSE))
error("InvalidateRgn failed %u", (unsigned)GetLastError());
} else {
rects = (RECTL *)rgndata->Buffer;
for (i = 0; i < rgndata->rdh.nCount; i++) {
struct rect xr;
xr.x1 = rects[i].xLeft;
xr.x2 = rects[i].xRight;
xr.y1 = rects[i].yTop;
xr.y2 = rects[i].yBottom;
/*fprintf(stderr, "returned: %d, %d, %d, %d\n", xr.x1, xr.x2, xr.y1, xr.y2);*/
add_to_rect_set(set, &xr);
}
}
end_rgndata:
pm_free(rgndata);
#endif
end:
pm_scroll_redraws(win, &dev->clip, scx, scy);
pm_unlock();
debug_call(("M: scroll done"));
return 0;
}
#ifdef OS2
#define pm_flush NULL
#endif
#ifdef WIN
static void pm_flush(struct graphics_device *dev)
{
PM_UNFLUSH();
pm_do_flush(NULL);
}
#endif
struct graphics_driver pmshell_driver = {
#ifdef OS2
cast_uchar "pmshell",
#endif
#ifdef WIN
cast_uchar "windows",
#endif
pm_init_driver,
pm_init_device,
pm_shutdown_device,
pm_shutdown_driver,
NULL,
NULL,
NULL,
pm_get_af_unix_name,
NULL,
NULL,
pm_get_empty_bitmap,
pm_register_bitmap,
pm_prepare_strip,
pm_commit_strip,
pm_unregister_bitmap,
pm_draw_bitmap,
NULL, /* pm_get_color */
pm_fill_area,
pm_draw_hline,
pm_draw_vline,
pm_scroll,
NULL,
pm_flush,
NULL, /* block */
NULL, /* unblock */
pm_set_palette,
NULL, /* get_real_colors */
pm_set_window_title,
NULL, /* exec */
NULL, /* set_clipboard_text */
NULL, /* get_clipboard_text */
0, /* depth */
0, 0, /* x, y */
GD_UNICODE_KEYS, /* flags */
NULL, /* param */
};
#ifdef _UWIN
int main(int argc, char **argv);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInst, LPSTR lpCmdLine, int nShow)
{
int i, neednew, quote, clen;
int argc = 1;
char **argv;
if (!(argv = malloc(2 * sizeof(char *)))) alloc_err: fatal_exit("can't allocate commandline");
if (!(argv[0] = cast_char strdup("links"))) goto alloc_err;
argv[1] = NULL;
neednew = 1;
quote = 0;
clen = 0;
for (i = 0; lpCmdLine[i]; i++) {
unsigned c = lpCmdLine[i];
if (c == ' ' && !quote) {
neednew = 1;
continue;
}
if (c == '"') {
quote ^= 1;
continue;
}
if (c == '\\' && lpCmdLine[i + 1]) {
c = lpCmdLine[++i];
}
if (neednew) {
if (!(argv = realloc(argv, (argc + 2) * sizeof(char *)))) goto alloc_err;
if (!(argv[argc] = malloc(1))) goto alloc_err;
argv[argc + 1] = NULL;
argc++;
neednew = 0;
clen = 0;
}
if (!(argv[argc - 1] = realloc(argv[argc - 1], clen + 1))) goto alloc_err;
argv[argc - 1][clen] = c;
argv[argc - 1][clen + 1] = 0;
clen++;
}
/*debug("cmdline: -%s-", lpCmdLine);*/
return main(argc, argv);
}
#endif
#endif