#include "gros2pm.h" #define INCL_DOS #define INCL_WIN #define INCL_GPI #define INCL_SUB #include #include #include #include static void Panic( const char* message ) { fprintf( stderr, "%s", message ); exit(1); } typedef struct Translator { ULONG os2key; grKey grkey; } Translator; static Translator key_translators[] = { { VK_BACKSPACE, grKeyBackSpace }, { VK_TAB, grKeyTab }, { VK_ENTER, grKeyReturn }, { VK_ESC, grKeyEsc }, { VK_HOME, grKeyHome }, { VK_LEFT, grKeyLeft }, { VK_UP, grKeyUp }, { VK_RIGHT, grKeyRight }, { VK_DOWN, grKeyDown }, { VK_PAGEUP, grKeyPageUp }, { VK_PAGEDOWN, grKeyPageDown }, { VK_END, grKeyEnd }, { VK_F1, grKeyF1 }, { VK_F2, grKeyF2 }, { VK_F3, grKeyF3 }, { VK_F4, grKeyF4 }, { VK_F5, grKeyF5 }, { VK_F6, grKeyF6 }, { VK_F7, grKeyF7 }, { VK_F8, grKeyF8 }, { VK_F9, grKeyF9 }, { VK_F10, grKeyF10 }, { VK_F11, grKeyF11 }, { VK_F12, grKeyF12 } }; #define MAX_PIXEL_MODES 32 static int num_pixel_modes = 0; static grPixelMode pixel_modes[ MAX_PIXEL_MODES ]; static int pixel_depth[ MAX_PIXEL_MODES ]; static HAB gr_anchor; /* device anchor block */ typedef POINTL PMBlitPoints[4]; typedef struct grPMSurface_ { grSurface root; grBitmap image; HAB anchor; /* handle to anchor block for surface's window */ HWND frame_window; /* handle to window's frame */ HWND client_window; /* handle to window's client */ HWND title_window; /* handle to window's title bar */ HPS image_ps; /* memory presentation space used to hold */ /* the surface's content under PM */ HDC image_dc; /* memory device context for the image */ HEV event_lock; /* semaphore used in listen_surface */ HMTX image_lock; /* a mutex used to synchronise access */ /* to the memory presentation space */ /* used to hold the surface */ TID message_thread; /* thread used to process this surface's */ /* messages.. */ PBITMAPINFO2 bitmap_header;/* os/2 bitmap descriptor */ HBITMAP os2_bitmap; /* Handle to OS/2 bitmap contained in image */ BOOL ready; /* ??? */ long shades[256]; /* indices of gray levels in pixel_mode_gray */ POINTL surface_blit[4]; /* surface blitting table */ POINTL magnify_blit[4]; /* magnifier blitting table */ int magnification; /* level of magnification */ POINTL magnify_center; SIZEL magnify_size; grEvent event; PMBlitPoints blit_points; } grPMSurface; static void enable_os2_iostreams( void ) { PTIB thread_block; PPIB process_block; /* XXX : This is a very nasty hack, it fools OS/2 and let the program */ /* call PM functions, even though stdin/stdout/stderr are still */ /* directed to the standard i/o streams.. */ /* The program must be compiled with WINDOWCOMPAT */ /* */ /* Credits go to Michal for finding this !! */ /* */ DosGetInfoBlocks( &thread_block, &process_block ); process_block->pib_ultype = 3; } static int init_device( void ) { enable_os2_iostreams(); /* create an anchor block. This will allow this thread (i.e. the */ /* main one) to call Gpi functions.. */ gr_anchor = WinInitialize(0); if (!gr_anchor) { /* could not initialise Presentation Manager */ return -1; } return 0; } static void done_device( void ) { /* Indicates that we do not use the Presentation Manager, this */ /* will also release all associated resources.. */ WinTerminate( gr_anchor ); } /* close a given window */ static void done_surface( grPMSurface* surface ) { if ( surface->frame_window ) WinDestroyWindow( surface->frame_window ); WinReleasePS( surface->image_ps ); grDoneBitmap( &surface->image ); grDoneBitmap( &surface->root.bitmap ); } static void add_pixel_mode( grPixelMode pixel_mode, int depth ) { if ( num_pixel_modes >= MAX_PIXEL_MODES ) Panic( "X11.Too many pixel modes\n" ); pixel_modes[ num_pixel_modes ] = pixel_mode; pixel_depth[ num_pixel_modes ] = depth; num_pixel_modes++; } #define LOCK(x) DosRequestMutexSem( x, SEM_INDEFINITE_WAIT ); #define UNLOCK(x) DosReleaseMutexSem( x ) static const int pixel_mode_bit_count[] = { 0, 1, 4, 8, /* pal8 */ 8, /* gray */ 15, 16, 24, 32 }; /************************************************************************ * * Technical note : how the OS/2 Presntation Manager driver works * * PM is, in my opinion, a bloated and over-engineered graphics * sub-system, even though it has lots of nice features. Here are * a few tidbits about it : * * * - under PM, a "bitmap" is a device-specific object whose bits are * not directly accessible to the client application. This means * that we must use a scheme like the following to display our * surfaces : * * - hold, for each surface, its own bitmap buffer where the * rest of MiGS writes directly. * * - create a PM bitmap object with the same dimensions (and * possibly format). * * - copy the content of each updated rectangle into the * PM bitmap with the function 'GpiSetBitmapBits'. * * - finally, "blit" the PM bitmap to the screen calling * 'GpiBlitBlt' * * - but there is more : you cannot directly blit a PM bitmap to the * screen with PM. The 'GpiBlitBlt' only works with presentation * spaces. This means that we also need to create, for each surface : * * - a memory presentation space, used to hold the PM bitmap * - a "memory device context" for the presentation space * * The blit is then performed from the memory presentation space * to the screen's presentation space.. * * * - because each surface creates its own event-handling thread, * we must protect the surface's presentation space from concurrent * accesses (i.e. calls to 'GpiSetBitmapBits' when drawing to the * surface, and calls to 'GpiBlitBlt' when drawing it on the screen * are performed in two different threads). * * we use a simple mutex to do this. * * * - we also use a semaphore to perform a rendez-vous between the * main and event-handling threads (needed in "listen_event"). * ************************************************************************/ static void RunPMWindow( grPMSurface* surface ); static void convert_gray_to_pal8( grPMSurface* surface, int x, int y, int w, int h ) { grBitmap* target = &surface->image; grBitmap* source = &surface->root.bitmap; byte* write = (byte*)target->buffer + y*target->pitch + x; byte* read = (byte*)source->buffer + y*source->pitch + x; long* palette = surface->shades; while (h > 0) { byte* _write = write; byte* _read = read; byte* limit = _write + w; for ( ; _write < limit; _write++, _read++ ) *_write = (byte) palette[ *_read ]; write += target->pitch; read += source->pitch; h--; } } static void convert_gray_to_16( grPMSurface* surface, int x, int y, int w, int h ) { grBitmap* target = &surface->image; grBitmap* source = &surface->root.bitmap; byte* write = (byte*)target->buffer + y*target->pitch + 2*x; byte* read = (byte*)source->buffer + y*source->pitch + x; long* palette = surface->shades; while (h > 0) { byte* _write = write; byte* _read = read; byte* limit = _write + 2*w; for ( ; _write < limit; _write += 2, _read++ ) *(short*)_write = (short)palette[ *_read ]; write += target->pitch; read += source->pitch; h--; } } static void convert_gray_to_24( grPMSurface* surface, int x, int y, int w, int h ) { grBitmap* target = &surface->image; grBitmap* source = &surface->root.bitmap; byte* write = (byte*)target->buffer + y*target->pitch + 3*x; byte* read = (byte*)source->buffer + y*source->pitch + x; while (h > 0) { byte* _write = write; byte* _read = read; byte* limit = _write + 3*w; for ( ; _write < limit; _write += 3, _read++ ) { byte color = *_read; _write[0] = _write[1] = _write[2] = color; } write += target->pitch; read += source->pitch; h--; } } static void convert_gray_to_32( grPMSurface* surface, int x, int y, int w, int h ) { grBitmap* target = &surface->image; grBitmap* source = &surface->root.bitmap; byte* write = (byte*)target->buffer + y*target->pitch + 4*x; byte* read = (byte*)source->buffer + y*source->pitch + x; while (h > 0) { byte* _write = write; byte* _read = read; byte* limit = _write + 4*w; for ( ; _write < limit; _write += 4, _read++ ) { byte color = *_read; _write[0] = _write[1] = _write[2] = _write[3] = color; } write += target->pitch; read += source->pitch; h--; } } static void convert_rectangle( grPMSurface* surface, int x, int y, int w, int h ) { int z; /* first of all, clip to the surface's area */ if ( x >= surface->image.width || x+w <= 0 || y >= surface->image.rows || y+h <= 0 ) return; if ( x < 0 ) { w += x; x = 0; } z = (x + w) - surface->image.width; if (z > 0) w -= z; z = (y + h) - surface->image.rows; if (z > 0) h -= z; /* convert the rectangle to the target depth for gray surfaces */ if (surface->root.bitmap.mode == gr_pixel_mode_gray) { switch (surface->image.mode) { case gr_pixel_mode_pal8 : convert_gray_to_pal8( surface, x, y, w, h ); break; case gr_pixel_mode_rgb555: case gr_pixel_mode_rgb565: convert_gray_to_16 ( surface, x, y, w, h ); break; case gr_pixel_mode_rgb24: convert_gray_to_24 ( surface, x, y, w, h ); break; case gr_pixel_mode_rgb32: convert_gray_to_32 ( surface, x, y, w, h ); break; default: ; } } } static void refresh_rectangle( grPMSurface* surface, int x, int y, int w, int h ) { convert_rectangle( surface, x, y, w, h ); WinInvalidateRect( surface->client_window, NULL, FALSE ); WinUpdateWindow( surface->frame_window ); } static void set_title( grPMSurface* surface, const char* title ) { WinSetWindowText( surface->title_window, (PSZ)title ); } static void listen_event( grPMSurface* surface, int event_mask, grEvent* grevent ) { ULONG ulRequestCount; (void) event_mask; /* ignored for now */ /* the listen_event function blocks until there is an event to process */ DosWaitEventSem( surface->event_lock, SEM_INDEFINITE_WAIT ); DosQueryEventSem( surface->event_lock, &ulRequestCount ); *grevent = surface->event; DosResetEventSem( surface->event_lock, &ulRequestCount ); return; } static int init_surface( grPMSurface* surface, grBitmap* bitmap ) { PBITMAPINFO2 bit; SIZEL sizl = { 0, 0 }; LONG palette[256]; /* create the bitmap - under OS/2, we support all modes as PM */ /* handles all conversions automatically.. */ if ( grNewBitmap( surface->root.bitmap.mode, surface->root.bitmap.grays, surface->root.bitmap.width, surface->root.bitmap.rows, bitmap ) ) return grError; surface->root.bitmap = *bitmap; /* create the image and event lock */ DosCreateEventSem( NULL, &surface->event_lock, 0, TRUE ); DosCreateMutexSem( NULL, &surface->image_lock, 0, FALSE ); /* create the image's presentation space */ surface->image_dc = DevOpenDC( gr_anchor, OD_MEMORY, (PSZ)"*", 0L, 0L, 0L ); surface->image_ps = GpiCreatePS( gr_anchor, surface->image_dc, &sizl, PU_PELS | GPIT_MICRO | GPIA_ASSOC | GPIF_DEFAULT ); GpiSetBackMix( surface->image_ps, BM_OVERPAINT ); /* create the image's PM bitmap */ bit = (PBITMAPINFO2)grAlloc( sizeof(BITMAPINFO2) + 256*sizeof(RGB2) ); surface->bitmap_header = bit; bit->cbFix = sizeof( BITMAPINFOHEADER2 ); bit->cx = surface->root.bitmap.width; bit->cy = surface->root.bitmap.rows; bit->cPlanes = 1; bit->argbColor[0].bBlue = 0; bit->argbColor[0].bGreen = 0; bit->argbColor[0].bRed = 0; bit->argbColor[1].bBlue = 255; bit->argbColor[1].bGreen = 255; bit->argbColor[1].bRed = 255; bit->cBitCount = pixel_mode_bit_count[ surface->root.bitmap.mode ]; surface->os2_bitmap = GpiCreateBitmap( surface->image_ps, (PBITMAPINFOHEADER2)bit, 0L, NULL, NULL ); GpiSetBitmap( surface->image_ps, surface->os2_bitmap ); bit->cbFix = sizeof( BITMAPINFOHEADER2 ); GpiQueryBitmapInfoHeader( surface->os2_bitmap, (PBITMAPINFOHEADER2)bit ); /* for gr_pixel_mode_gray, create a gray-levels logical palette */ if ( bitmap->mode == gr_pixel_mode_gray ) { int x, count; count = bitmap->grays; for ( x = 0; x < count; x++ ) palette[x] = (((count-x)*255)/count) * 0x010101; /* create logical color table */ GpiCreateLogColorTable( surface->image_ps, (ULONG) LCOL_PURECOLOR, (LONG) LCOLF_CONSECRGB, (LONG) 0L, (LONG) count, (PLONG) palette ); /* now, copy the color indexes to surface->shades */ for ( x = 0; x < count; x++ ) surface->shades[x] = GpiQueryColorIndex( surface->image_ps, 0, palette[x] ); } /* set up the blit points array */ surface->blit_points[1].x = surface->root.bitmap.width; surface->blit_points[1].y = surface->root.bitmap.rows; surface->blit_points[3] = surface->blit_points[1]; /* Finally, create the event handling thread for the surface's window */ DosCreateThread( &surface->message_thread, (PFNTHREAD) RunPMWindow, (ULONG) surface, 0UL, 32920 ); surface->root.done = (grDoneSurfaceFunc) done_surface; surface->root.refresh_rect = (grRefreshRectFunc) refresh_rectangle; surface->root.set_title = (grSetTitleFunc) set_title; surface->root.listen_event = (grListenEventFunc) listen_event; convert_rectangle( surface, 0, 0, bitmap->width, bitmap->rows ); return 0; } MRESULT EXPENTRY Message_Process( HWND handle, ULONG mess, MPARAM parm1, MPARAM parm2 ); static void RunPMWindow( grPMSurface* surface ) { unsigned char class_name[] = "DisplayClass"; ULONG class_flags; static HMQ queue; QMSG message; /* create an anchor to allow this thread to use PM */ surface->anchor = WinInitialize(0); if (!surface->anchor) { printf( "Error doing WinInitialize()\n" ); return; } /* create a message queue */ queue = WinCreateMsgQueue( surface->anchor, 0 ); if (!queue) { printf( "Error doing >inCreateMsgQueue()\n" ); return; } /* register the window class */ if ( !WinRegisterClass( surface->anchor, (PSZ) class_name, (PFNWP) Message_Process, CS_SIZEREDRAW, 0 ) ) { printf( "Error doing WinRegisterClass()\n" ); return; } /* create the PM window */ class_flags = FCF_TITLEBAR | FCF_MINBUTTON | FCF_DLGBORDER | FCF_TASKLIST | FCF_SYSMENU; surface->frame_window = WinCreateStdWindow( HWND_DESKTOP, WS_VISIBLE, &class_flags, (PSZ) class_name, (PSZ) "FreeType PM Graphics", WS_VISIBLE, 0, 0, &surface->client_window ); if (!surface->frame_window) { printf( "Error doing WinCreateStdWindow()\n" ); return; } /* find the title window handle */ surface->title_window = WinWindowFromID( surface->frame_window, FID_TITLEBAR ); /* set Window size and position */ WinSetWindowPos( surface->frame_window, 0L, (SHORT) 60, (SHORT) WinQuerySysValue( HWND_DESKTOP, SV_CYSCREEN ) - surface->root.bitmap.rows + 100, (SHORT) WinQuerySysValue( HWND_DESKTOP, SV_CYDLGFRAME )*2 + surface->root.bitmap.width, (SHORT) WinQuerySysValue( HWND_DESKTOP, SV_CYTITLEBAR ) + WinQuerySysValue( HWND_DESKTOP, SV_CYDLGFRAME )*2 + surface->root.bitmap.rows, SWP_SIZE | SWP_MOVE ); /* save the handle to the current surface within the window words */ WinSetWindowPtr( surface->frame_window,QWL_USER, surface ); /* run the message queue till the end */ while ( WinGetMsg( surface->anchor, &message, (HWND)NULL, 0, 0 ) ) WinDispatchMsg( surface->anchor, &message ); /* clean-up */ WinDestroyWindow( surface->frame_window ); surface->frame_window = 0; WinDestroyMsgQueue( queue ); WinTerminate( surface->anchor ); /* await death... */ while ( 1 ) DosSleep( 100 ); } /* Message processing for our PM Window class */ MRESULT EXPENTRY Message_Process( HWND handle, ULONG mess, MPARAM parm1, MPARAM parm2 ) { static HDC screen_dc; static HPS screen_ps; static BOOL minimized; SIZEL sizl; SWP swp; grPMSurface* surface; /* get the handle to the window's surface */ surface = (grPMSurface*)WinQueryWindowPtr( handle, QWL_USER ); switch( mess ) { case WM_DESTROY: /* warn the main thread to quit if it didn't know */ surface->event.type = gr_event_key; surface->event.key = grKeyEsc; DosPostEventSem( surface->event_lock ); break; case WM_CREATE: /* set original magnification */ minimized = FALSE; /* create Device Context and Presentation Space for screen. */ screen_dc = WinOpenWindowDC( handle ); screen_ps = GpiCreatePS( surface->anchor, screen_dc, &sizl, PU_PELS | GPIT_MICRO | GPIA_ASSOC | GPIF_DEFAULT ); /* take the input focus */ WinFocusChange( HWND_DESKTOP, handle, 0L ); break; case WM_MINMAXFRAME: /* to update minimized if changed */ swp = *((PSWP) parm1); if ( swp.fl & SWP_MINIMIZE ) minimized = TRUE; if ( swp.fl & SWP_RESTORE ) minimized = FALSE; return WinDefWindowProc( handle, mess, parm1, parm2 ); break; case WM_ERASEBACKGROUND: case WM_PAINT: /* copy the memory image of the screen out to the real screen */ DosRequestMutexSem( surface->image_lock, SEM_INDEFINITE_WAIT ); WinBeginPaint( handle, screen_ps, NULL ); /* main image and magnified picture */ GpiBitBlt( screen_ps, surface->image_ps, 4L, surface->blit_points, ROP_SRCCOPY, BBO_AND ); WinEndPaint( screen_ps ); DosReleaseMutexSem( surface->image_lock ); break; case WM_CHAR: if ( CHARMSG( &mess )->fs & KC_KEYUP ) break; /* look for a specific vkey */ { int count = sizeof( key_translators )/sizeof( key_translators[0] ); Translator* trans = key_translators; Translator* limit = trans + count; for ( ; trans < limit; trans++ ) if ( CHARMSG(&mess)->vkey == trans->os2key ) { surface->event.key = trans->grkey; goto Do_Key_Event; } } /* otherwise, simply record the character code */ if ( (CHARMSG( &mess )->fs & KC_CHAR) == 0 ) break; surface->event.key = CHARMSG(&mess)->chr; Do_Key_Event: surface->event.type = gr_event_key; DosPostEventSem( surface->event_lock ); break; default: return WinDefWindowProc( handle, mess, parm1, parm2 ); } return (MRESULT) FALSE; } #if 0 static grKey KeySymTogrKey( key ) { grKey k; int count = sizeof(key_translators)/sizeof(key_translators[0]); Translator* trans = key_translators; Translator* limit = trans + count; k = grKeyNone; while ( trans < limit ) { if ( trans->xkey == key ) { k = trans->grkey; break; } trans++; } return k; } static void listen_event( grPMSurface* surface, int event_mask, grEvent* grevent ) { grKey grkey; /* XXXX : For now, ignore the event mask, and only exit when */ /* a key is pressed.. */ (void)event_mask; /* Now, translate the keypress to a grKey */ /* If this wasn't part of the simple translated keys, simply get the charcode */ /* from the character buffer */ grkey = grKEY(key_buffer[key_cursor++]); Set_Key: grevent->type = gr_key_down; grevent->key = grkey; } #endif grDevice gr_os2pm_device = { sizeof( grPMSurface ), "os2pm", init_device, done_device, (grDeviceInitSurfaceFunc) init_surface, 0, 0 };