--- BasiliskII/src/Unix/video_x.cpp 2002/09/28 12:42:39 1.68 +++ BasiliskII/src/Unix/video_x.cpp 2008/01/01 09:40:33 1.84 @@ -1,7 +1,7 @@ /* * video_x.cpp - Video/graphics emulation, X11 specific stuff * - * Basilisk II (C) 1997-2002 Christian Bauer + * Basilisk II (C) 1997-2008 Christian Bauer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -62,6 +62,7 @@ #include "prefs.h" #include "user_strings.h" #include "video.h" +#include "video_blit.h" #define DEBUG 0 #include "debug.h" @@ -98,6 +99,7 @@ static bool redraw_thread_active = false #ifdef HAVE_PTHREADS static pthread_attr_t redraw_thread_attr; // Redraw thread attributes static volatile bool redraw_thread_cancel; // Flag: Cancel Redraw thread +static volatile bool redraw_thread_cancel_ack; // Flag: Acknowledge for redraw thread cancellation static pthread_t redraw_thread; // Redraw thread #endif @@ -122,6 +124,8 @@ static bool use_keycodes = false; // static int keycode_table[256]; // X keycode -> Mac keycode translation table // X11 variables +char *x_display_name = NULL; // X11 display name +Display *x_display = NULL; // X11 display handle static int screen; // Screen number static Window rootwin; // Root window and our window static int num_depths = 0; // Number of available X depths @@ -131,15 +135,17 @@ static unsigned long black_pixel, white_ static int eventmask; static int xdepth; // Depth of X screen +static VisualFormat visualFormat; static XVisualInfo visualInfo; static Visual *vis; static int color_class; +static bool x_native_byte_order; // XImage has native byte order? static int rshift, rloss, gshift, gloss, bshift, bloss; // Pixel format of DirectColor/TrueColor modes static Colormap cmap[2] = {0, 0}; // Colormaps for indexed modes (DGA needs two of them) -static XColor x_palette[256]; // Color palette to be used as CLUT and gamma table +static XColor x_palette[256]; // Color palette to be used as CLUT and gamma table static bool x_palette_changed = false; // Flag: Palette changed, redraw thread must set new colors #ifdef ENABLE_FBDEV_DGA @@ -192,6 +198,10 @@ extern Display *x_display; // From sys_unix.cpp extern void SysMountFirstFloppy(void); +// From clip_unix.cpp +extern void ClipboardSelectionClear(XSelectionClearEvent *); +extern void ClipboardSelectionRequest(XSelectionRequestEvent *); + /* * monitor_desc subclass for X11 display @@ -214,10 +224,54 @@ public: * Utility functions */ +// Map video_mode depth ID to numerical depth value +static inline int depth_of_video_mode(video_mode const & mode) +{ + int depth = -1; + switch (mode.depth) { + case VDEPTH_1BIT: + depth = 1; + break; + case VDEPTH_2BIT: + depth = 2; + break; + case VDEPTH_4BIT: + depth = 4; + break; + case VDEPTH_8BIT: + depth = 8; + break; + case VDEPTH_16BIT: + depth = 16; + break; + case VDEPTH_32BIT: + depth = 32; + break; + default: + abort(); + } + return depth; +} + // Map RGB color to pixel value (this only works in TrueColor/DirectColor visuals) -static inline uint32 map_rgb(uint8 red, uint8 green, uint8 blue) +static inline uint32 map_rgb(uint8 red, uint8 green, uint8 blue, bool fix_byte_order = false) { - return ((red >> rloss) << rshift) | ((green >> gloss) << gshift) | ((blue >> bloss) << bshift); + uint32 val = ((red >> rloss) << rshift) | ((green >> gloss) << gshift) | ((blue >> bloss) << bshift); + if (fix_byte_order && !x_native_byte_order) { + // We have to fix byte order in the ExpandMap[] + // NOTE: this is only an optimization since Screen_blitter_init() + // could be arranged to choose an NBO or OBO (with + // byteswapping) Blit_Expand_X_To_Y() function + switch (visualFormat.depth) { + case 15: case 16: + val = do_byteswap_16(val); + break; + case 24: case 32: + val = do_byteswap_32(val); + break; + } + } + return val; } // Do we have a visual for handling the specified Mac depth? If so, set the @@ -443,6 +497,33 @@ static int error_handler(Display *d, XEr /* + * Framebuffer allocation routines + */ + +#ifdef ENABLE_VOSF +#include "vm_alloc.h" + +static void *vm_acquire_framebuffer(uint32 size) +{ + // always try to allocate framebuffer at the same address + static void *fb = VM_MAP_FAILED; + if (fb != VM_MAP_FAILED) { + if (vm_acquire_fixed(fb, size) < 0) + fb = VM_MAP_FAILED; + } + if (fb == VM_MAP_FAILED) + fb = vm_acquire(size, VM_MAP_DEFAULT | VM_MAP_32BIT); + return fb; +} + +static inline void vm_release_framebuffer(void *fb, uint32 size) +{ + vm_release(fb, size); +} +#endif + + +/* * Display "driver" classes */ @@ -503,6 +584,36 @@ private: int mouse_last_x, mouse_last_y; // Last mouse position (for relative mode) }; +class driver_dga; +static void update_display_dga_vosf(driver_dga *drv); + +class driver_dga : public driver_base { + friend void update_display_dga_vosf(driver_dga *drv); + +public: + driver_dga(X11_monitor_desc &monitor); + ~driver_dga(); + + void suspend(void); + void resume(void); + +protected: + struct FakeXImage { + int width, height; // size of image + int depth; // depth of image + int bytes_per_line; // accelerator to next line + + FakeXImage(int w, int h, int d) + : width(w), height(h), depth(d) + { bytes_per_line = TrivialBytesPerRow(width, DepthModeForPixelDepth(depth)); } + }; + FakeXImage *img; + +private: + Window suspend_win; // "Suspend" information window + void *fb_save; // Saved frame buffer for suspend/resume +}; + static driver_base *drv = NULL; // Pointer to currently used driver object #ifdef ENABLE_VOSF @@ -547,7 +658,7 @@ driver_base::~driver_base() // the_buffer shall always be mapped through vm_acquire() so that we can vm_protect() it at will if (the_buffer != VM_MAP_FAILED) { D(bug(" releasing the_buffer at %p (%d bytes)\n", the_buffer, the_buffer_size)); - vm_release(the_buffer, the_buffer_size); + vm_release_framebuffer(the_buffer, the_buffer_size); the_buffer = NULL; } if (the_host_buffer) { @@ -697,7 +808,7 @@ driver_window::driver_window(X11_monitor // Allocate memory for frame buffer (SIZE is extended to page-boundary) the_host_buffer = the_buffer_copy; the_buffer_size = page_extend((aligned_height + 2) * img->bytes_per_line); - the_buffer = (uint8 *)vm_acquire(the_buffer_size); + the_buffer = (uint8 *)vm_acquire_framebuffer(the_buffer_size); the_buffer_copy = (uint8 *)malloc(the_buffer_size); D(bug("the_buffer = %p, the_buffer_copy = %p, the_host_buffer = %p\n", the_buffer, the_buffer_copy, the_host_buffer)); #else @@ -718,18 +829,12 @@ driver_window::driver_window(X11_monitor XDefineCursor(x_display, w, mac_cursor); // Init blitting routines - bool native_byte_order; -#ifdef WORDS_BIGENDIAN - native_byte_order = (XImageByteOrder(x_display) == MSBFirst); -#else - native_byte_order = (XImageByteOrder(x_display) == LSBFirst); -#endif #ifdef ENABLE_VOSF - Screen_blitter_init(&visualInfo, native_byte_order, mode.depth); + Screen_blitter_init(visualFormat, x_native_byte_order, depth_of_video_mode(mode)); #endif // Set frame buffer base - set_mac_frame_buffer(monitor, mode.depth, native_byte_order); + set_mac_frame_buffer(monitor, mode.depth, x_native_byte_order); // Everything went well init_ok = true; @@ -844,21 +949,8 @@ void driver_window::mouse_moved(int x, i * DGA display driver base class */ -class driver_dga : public driver_base { -public: - driver_dga(X11_monitor_desc &monitor); - ~driver_dga(); - - void suspend(void); - void resume(void); - -private: - Window suspend_win; // "Suspend" information window - void *fb_save; // Saved frame buffer for suspend/resume -}; - driver_dga::driver_dga(X11_monitor_desc &m) - : driver_base(m), suspend_win(0), fb_save(NULL) + : driver_base(m), suspend_win(0), fb_save(NULL), img(NULL) { } @@ -866,6 +958,9 @@ driver_dga::~driver_dga() { XUngrabPointer(x_display, CurrentTime); XUngrabKeyboard(x_display, CurrentTime); + + if (img) + delete img; } // Suspend emulation @@ -1086,14 +1181,17 @@ driver_fbdev::driver_fbdev(X11_monitor_d #if REAL_ADDRESSING || DIRECT_ADDRESSING // Screen_blitter_init() returns TRUE if VOSF is mandatory // i.e. the framebuffer update function is not Blit_Copy_Raw - use_vosf = Screen_blitter_init(&visualInfo, true, mode.depth); + use_vosf = Screen_blitter_init(visualFormat, true, mode.depth); if (use_vosf) { // Allocate memory for frame buffer (SIZE is extended to page-boundary) the_host_buffer = the_buffer; the_buffer_size = page_extend((height + 2) * bytes_per_row); the_buffer_copy = (uint8 *)malloc(the_buffer_size); - the_buffer = (uint8 *)vm_acquire(the_buffer_size); + the_buffer = (uint8 *)vm_acquire_framebuffer(the_buffer_size); + + // Fake image for DGA/VOSF mode to know about display bounds + img = new FakeXImage(width, height, depth_of_video_mode(mode)); } #else use_vosf = false; @@ -1217,23 +1315,20 @@ driver_xf86dga::driver_xf86dga(X11_monit // Init blitting routines int bytes_per_row = TrivialBytesPerRow((v_width + 7) & ~7, mode.depth); #if ENABLE_VOSF - bool native_byte_order; -#ifdef WORDS_BIGENDIAN - native_byte_order = (XImageByteOrder(x_display) == MSBFirst); -#else - native_byte_order = (XImageByteOrder(x_display) == LSBFirst); -#endif #if REAL_ADDRESSING || DIRECT_ADDRESSING // Screen_blitter_init() returns TRUE if VOSF is mandatory // i.e. the framebuffer update function is not Blit_Copy_Raw - use_vosf = Screen_blitter_init(&visualInfo, native_byte_order, mode.depth); + use_vosf = Screen_blitter_init(visualFormat, x_native_byte_order, depth_of_video_mode(mode)); if (use_vosf) { // Allocate memory for frame buffer (SIZE is extended to page-boundary) the_host_buffer = the_buffer; the_buffer_size = page_extend((height + 2) * bytes_per_row); the_buffer_copy = (uint8 *)malloc(the_buffer_size); - the_buffer = (uint8 *)vm_acquire(the_buffer_size); + the_buffer = (uint8 *)vm_acquire_framebuffer(the_buffer_size); + + // Fake image for DGA/VOSF mode to know about display bounds + img = new FakeXImage((v_width + 7) & ~7, height, depth_of_video_mode(mode)); } #else use_vosf = false; @@ -1315,6 +1410,10 @@ static void keycode_init(void) // Search for server vendor string, then read keycodes const char *vendor = ServerVendor(x_display); + // Force use of MacX mappings on MacOS X with Apple's X server + int dummy; + if (XQueryExtension(x_display, "Apple-DRI", &dummy, &dummy, &dummy)) + vendor = "MacX"; bool vendor_found = false; char line[256]; while (fgets(line, 255, f)) { @@ -1368,6 +1467,20 @@ bool X11_monitor_desc::video_open(void) return false; } + // Determine the byte order of an XImage content +#ifdef WORDS_BIGENDIAN + x_native_byte_order = (XImageByteOrder(x_display) == MSBFirst); +#else + x_native_byte_order = (XImageByteOrder(x_display) == LSBFirst); +#endif + + // Build up visualFormat structure + visualFormat.fullscreen = (display_type == DISPLAY_DGA); + visualFormat.depth = visualInfo.depth; + visualFormat.Rmask = visualInfo.red_mask; + visualFormat.Gmask = visualInfo.green_mask; + visualFormat.Bmask = visualInfo.blue_mask; + // Create color maps if (color_class == PseudoColor || color_class == DirectColor) { cmap[0] = XCreateColormap(x_display, rootwin, vis, AllocAll); @@ -1428,7 +1541,7 @@ bool X11_monitor_desc::video_open(void) // Load gray ramp to 8->16/32 expand map if (!IsDirectMode(mode) && xdepth > 8) for (int i=0; i<256; i++) - ExpandMap[i] = map_rgb(i, i, i); + ExpandMap[i] = map_rgb(i, i, i, true); #endif // Create display driver object of requested type @@ -1473,7 +1586,7 @@ bool X11_monitor_desc::video_open(void) LOCK_FRAME_BUFFER; // Start redraw/input thread -#ifdef HAVE_PTHREADS +#ifdef USE_PTHREADS_SERVICES redraw_thread_cancel = false; Set_pthread_attr(&redraw_thread_attr, 0); redraw_thread_active = (pthread_create(&redraw_thread, &redraw_thread_attr, redraw_func, NULL) == 0); @@ -1669,13 +1782,12 @@ void X11_monitor_desc::video_close(void) D(bug("video_close()\n")); // Stop redraw thread -#ifdef HAVE_PTHREADS +#ifdef USE_PTHREADS_SERVICES if (redraw_thread_active) { redraw_thread_cancel = true; -#ifdef HAVE_PTHREAD_CANCEL - pthread_cancel(redraw_thread); -#endif + redraw_thread_cancel_ack = false; pthread_join(redraw_thread, NULL); + while (!redraw_thread_cancel_ack) ; } #endif redraw_thread_active = false; @@ -1798,7 +1910,7 @@ void X11_monitor_desc::set_palette(uint8 if (!IsDirectMode(mode) && xdepth > 8) { for (int i=0; i<256; i++) { int c = i & (num_in-1); // If there are less than 256 colors, we repeat the first entries (this makes color expansion easier) - ExpandMap[i] = map_rgb(pal[c*3+0], pal[c*3+1], pal[c*3+2]); + ExpandMap[i] = map_rgb(pal[c*3+0], pal[c*3+1], pal[c*3+2], true); } // We have to redraw everything because the interpretation of pixel values changed @@ -2003,10 +2115,28 @@ static int event2keycode(XKeyEvent &ev, static void handle_events(void) { - while (XPending(x_display)) { + for (;;) { XEvent event; - XNextEvent(x_display, &event); + XDisplayLock(); + + if (!XCheckMaskEvent(x_display, eventmask, &event)) { + // Handle clipboard events + if (XCheckTypedEvent(x_display, SelectionRequest, &event)) + ClipboardSelectionRequest(&event.xselectionrequest); + else if (XCheckTypedEvent(x_display, SelectionClear, &event)) + ClipboardSelectionClear(&event.xselectionclear); + // Window "close" widget clicked + else if (XCheckTypedEvent(x_display, ClientMessage, &event)) { + if (event.xclient.format == 32 && event.xclient.data.l[0] == WM_DELETE_WINDOW) { + ADBKeyDown(0x7f); // Power key + ADBKeyUp(0x7f); + } + } + XDisplayUnlock(); + break; + } + switch (event.type) { // Mouse button @@ -2114,15 +2244,9 @@ static void handle_events(void) memset(the_buffer_copy, 0, mode.bytes_per_row * mode.y); } break; - - // Window "close" widget clicked - case ClientMessage: - if (event.xclient.format == 32 && event.xclient.data.l[0] == WM_DELETE_WINDOW) { - ADBKeyDown(0x7f); // Power key - ADBKeyUp(0x7f); - } - break; } + + XDisplayUnlock(); } } @@ -2167,6 +2291,7 @@ static void update_display_dynamic(int t } } + XDisplayLock(); if ((nr_boxes <= max_box) && (nr_boxes)) { for (y1=0; y1<16; y1++) { for (x1=0; x1<16; x1++) { @@ -2220,6 +2345,7 @@ static void update_display_dynamic(int t } nr_boxes = 0; } + XDisplayUnlock(); } // Static display update (fixed frame rate, but incremental) @@ -2329,12 +2455,14 @@ static void update_display_static(driver } // Refresh display + XDisplayLock(); if (high && wide) { if (drv->have_shm) XShmPutImage(x_display, drv->w, drv->gc, drv->img, x1, y1, x1, y1, wide, high, 0); else XPutImage(x_display, drv->w, drv->gc, drv->img, x1, y1, x1, y1, wide, high); } + XDisplayUnlock(); } @@ -2376,7 +2504,9 @@ static inline void handle_palette_change if (x_palette_changed) { x_palette_changed = false; + XDisplayLock(); drv->update_palette(); + XDisplayUnlock(); } UNLOCK_PALETTE; @@ -2401,7 +2531,7 @@ static void video_refresh_dga_vosf(void) tick_counter = 0; if (mainBuffer.dirty) { LOCK_VOSF; - update_display_dga_vosf(); + update_display_dga_vosf(static_cast(drv)); UNLOCK_VOSF; } } @@ -2418,10 +2548,12 @@ static void video_refresh_window_vosf(vo if (++tick_counter >= frame_skip) { tick_counter = 0; if (mainBuffer.dirty) { + XDisplayLock(); LOCK_VOSF; update_display_window_vosf(static_cast(drv)); UNLOCK_VOSF; XSync(x_display, false); // Let the server catch up + XDisplayUnlock(); } } } @@ -2501,7 +2633,7 @@ void VideoRefresh(void) const int VIDEO_REFRESH_HZ = 60; const int VIDEO_REFRESH_DELAY = 1000000 / VIDEO_REFRESH_HZ; -#ifdef HAVE_PTHREADS +#ifdef USE_PTHREADS_SERVICES static void *redraw_func(void *arg) { int fd = ConnectionNumber(x_display); @@ -2542,7 +2674,9 @@ static void *redraw_func(void *arg) } uint64 end = GetTicks_usec(); - D(bug("%Ld refreshes in %Ld usec = %f refreshes/sec\n", ticks, end - start, ticks * 1000000.0 / (end - start))); + D(bug("%lld refreshes in %lld usec = %f refreshes/sec\n", ticks, end - start, ticks * 1000000.0 / (end - start))); + + redraw_thread_cancel_ack = true; return NULL; } #endif