/**********************************************************************
This file is part of Crack dot Com's free source code release of
Golgotha.
for
information about compiling & licensing issues visit this URL
If that doesn't help, contact Jonathan Clark at golgotha_source@usa.net (Subject should have "GOLG" in it) ***********************************************************************/ #include "video/display.hh" #include "video/x11/x11devs.hh" #include "device/kernel.hh" #include "palette/pal.hh" #include "error/error.hh" #include "memory/malloc.hh" #include "device/keys.hh" #include "time/profile.hh" #include#include #include x11_display_class x11_display_instance; static Cursor CreateNullCursor(Display *display, Window root) // makes a null cursor { Pixmap cursormask; XGCValues xgc; GC gc; XColor dummycolour; Cursor cursor; cursormask = XCreatePixmap(display, root, 1, 1, 1/*depth*/); xgc.function = GXclear; gc = XCreateGC(display, cursormask, GCFunction, &xgc); XFillRectangle(display, cursormask, gc, 0, 0, 1, 1); dummycolour.pixel = 0; dummycolour.red = 0; dummycolour.flags = 04; cursor = XCreatePixmapCursor(display, cursormask, cursormask, &dummycolour,&dummycolour, 0,0); XFreePixmap(display,cursormask); XFreeGC(display,gc); return cursor; } i4_bool x11_display_class::set_mouse_shape(i4_cursor_class *cursor) { if (mouse_pict) { delete mouse_pict; delete mouse_save; } mouse_trans=cursor->trans; mouse_pict=cursor->pict->copy(); mouse_save=new I4_SCREEN_TYPE(mouse_pict->width(),mouse_pict->height(),screen->get_pal()); mouse_hot_x=cursor->hot_x; mouse_hot_y=cursor->hot_y; return i4_T; } enum { devices_handled = i4_device_class::FLAG_MOUSE_BUTTON_UP | i4_device_class::FLAG_MOUSE_BUTTON_DOWN | i4_device_class::FLAG_MOUSE_MOVE | i4_device_class::FLAG_KEY_PRESS | i4_device_class::FLAG_KEY_RELEASE | i4_device_class::FLAG_DISPLAY_CHANGE }; void x11_display_class::x11_device_manager::get_x_time(w32 xtick, i4_time_class &t) { if (need_first_time) { first_time=xtick; need_first_time=i4_F; } w32 xtime=xtick-first_time; i4_time_class now; i4_time_class x_now(xtime); sw32 diff=x_now.milli_diff(now); if (diff>x11_start_time_best_diff) { x11_start_time_best_diff=diff; x11_start_time=xtime; i4_start_time=now; } t=i4_start_time; t.add_milli(xtime-x11_start_time); } i4_bool x11_display_class::x11_device_manager::process_events() // returns true if an event was dispatched { i4_bool motion_occured=i4_F; sw32 final_x, final_y; if (parent->display) { while (XPending(parent->display)) { XEvent xev; XNextEvent(parent->display,&xev); if (parent->shm_extension) parent->shm_extension->note_event(xev); switch (xev.type) { case Expose : { if (parent->context) { parent->context->add_both_dirty(xev.xexpose.x, xev.xexpose.y, xev.xexpose.x+xev.xexpose.width, xev.xexpose.y+xev.xexpose.height); } } break; //{{{ not implemented yet case ConfigureNotify : { XFlush(parent->display); int new_width=xev.xconfigure.width&~3; // must be word alligned int new_height=xev.xconfigure.height; if (new_width!=xev.xconfigure.width) XResizeWindow(parent->display,parent->mainwin,new_width,xev.xconfigure.height); XFlush(parent->display); parent->destroy_X_image(); parent->create_X_image(new_width, new_height); i4_display_change_event_class d_change(parent, i4_display_change_event_class::SIZE_CHANGE); send_event_to_agents(&d_change,FLAG_DISPLAY_CHANGE); } break; case MotionNotify : { motion_occured=i4_T; final_x=xev.xmotion.x; final_y=xev.xmotion.y; } break; case ButtonRelease : { i4_mouse_button_up_event_class::btype but; switch (xev.xbutton.button) { case 1 : but=i4_mouse_button_up_event_class::LEFT; break; case 3 : but=i4_mouse_button_up_event_class::RIGHT; break; case 2 : but=i4_mouse_button_up_event_class::CENTER; break; } i4_mouse_button_up_event_class up(but); send_event_to_agents(&up,FLAG_MOUSE_BUTTON_UP); } break; case ButtonPress : { i4_mouse_button_down_event_class::btype but; switch (xev.xbutton.button) { case 1 : but=i4_mouse_button_down_event_class::LEFT; break; case 3 : but=i4_mouse_button_down_event_class::RIGHT; break; case 2 : but=i4_mouse_button_down_event_class::CENTER; break; } i4_mouse_button_down_event_class down(but); send_event_to_agents(&down,FLAG_MOUSE_BUTTON_DOWN); } break; case KeyPress : case KeyRelease : { char buf; KeySym ks; XLookupString(&xev.xkey,&buf,1,&ks,NULL); w16 key, key_code; switch (ks) { case XK_Down : key_code=I4_DOWN; break; case XK_Up : key_code=I4_UP; break; case XK_Left : key_code=I4_LEFT; break; case XK_Right : key_code=I4_RIGHT; break; case XK_Control_L : { key_code=I4_CTRL_L; if (xev.type==KeyPress) modifier_state|=I4_MODIFIER_CTRL_L; else modifier_state&=~I4_MODIFIER_CTRL_L; } break; case XK_Control_R : { key_code=I4_CTRL_R; if (xev.type==KeyPress) modifier_state|=I4_MODIFIER_CTRL_R; else modifier_state&=~I4_MODIFIER_CTRL_R; } break; case XK_Alt_L : { key_code=I4_ALT_L; if (xev.type==KeyPress) modifier_state|=I4_MODIFIER_ALT_L; else modifier_state&=~I4_MODIFIER_ALT_L; } break; case XK_Alt_R : { key_code=I4_ALT_R; if (xev.type==KeyPress) modifier_state|=I4_MODIFIER_ALT_R; else modifier_state&=~I4_MODIFIER_ALT_R; } break; case XK_Shift_L : { key_code=I4_SHIFT_L; if (xev.type==KeyPress) modifier_state|=I4_MODIFIER_SHIFT_L; else modifier_state&=~I4_MODIFIER_SHIFT_L; } break; case XK_Shift_R : { key_code=I4_SHIFT_R; if (xev.type==KeyPress) modifier_state|=I4_MODIFIER_SHIFT_R; else modifier_state&=~I4_MODIFIER_SHIFT_R; } break; case XK_Num_Lock : key_code=I4_NUM_LOCK; break; case XK_Home : key_code=I4_HOME; break; case XK_End : key_code=I4_END; break; case XK_BackSpace : key_code=I4_BACKSPACE; break; case XK_Tab : key_code=I4_TAB; break; case XK_Return : key_code=I4_ENTER; break; case XK_Caps_Lock : key_code=I4_CAPS; break; case XK_Escape : key_code=I4_ESC; break; case XK_F1 : key_code=I4_F1; break; case XK_F2 : key_code=I4_F2; break; case XK_F3 : key_code=I4_F3; break; case XK_F4 : key_code=I4_F4; break; case XK_F5 : key_code=I4_F5; break; case XK_F6 : key_code=I4_F6; break; case XK_F7 : key_code=I4_F7; break; case XK_F8 : key_code=I4_F8; break; case XK_F9 : key_code=I4_F9; break; case XK_F10 : key_code=I4_F10; break; case XK_Insert : key_code=I4_INSERT; break; case XK_Page_Up : key_code=I4_PAGEUP; break; case XK_Page_Down : key_code=I4_PAGEDOWN; break; case XK_Delete : key_code=I4_DEL; break; case XK_KP_0 : key_code=I4_KP0; break; case XK_KP_1 : key_code=I4_KP1; break; case XK_KP_2 : key_code=I4_KP2; break; case XK_KP_3 : key_code=I4_KP3; break; case XK_KP_4 : key_code=I4_KP4; break; case XK_KP_5 : key_code=I4_KP5; break; case XK_KP_6 : key_code=I4_KP6; break; case XK_KP_7 : key_code=I4_KP7; break; case XK_KP_8 : key_code=I4_KP8; break; case XK_KP_9 : key_code=I4_KP9; break; case ' ': key_code=I4_SPACE; break; case '`': case '~': key_code='`'; break; case '-': case '_': key_code='-'; break; case '=': case '+': key_code='='; break; case '[': case '{': key_code='['; break; case ']': case '}': key_code=']'; break; case '\\': case '|': key_code='\\'; break; case ';': case ':': key_code=';'; break; case '\'': case '"': key_code='\''; break; case ',': case '<': key_code=','; break; case '.': case '>': key_code='.'; break; case '/': case '?': key_code='/'; break; case ')': key_code='0'; break; case '!': key_code='1'; break; case '@': key_code='2'; break; case '#': key_code='3'; break; case '$': key_code='4'; break; case '%': key_code='5'; break; case '^': key_code='6'; break; case '&': key_code='7'; break; case '*': key_code='8'; break; case '(': key_code='9'; break; default : if ((ks>=XK_A && ks<=XK_Z) || (ks>=XK_0 && ks<=XK_9)) key_code = ks; else if (ks>=XK_a && ks<=XK_z) key_code = ks + 'A' - 'a'; else key_code=0; } if (key_code) { if (xev.type==KeyPress) { i4_time_class t; get_x_time(xev.xkey.time, t); key = i4_key_translate(key_code,1,modifier_state); i4_key_press_event_class ev(key, key_code, modifier_state, t); send_event_to_agents(&ev,FLAG_KEY_PRESS); } else { key = i4_key_translate(key_code,0, modifier_state); i4_time_class t; get_x_time(xev.xkey.time, t); i4_key_release_event_class ev(key, key_code, modifier_state, t); send_event_to_agents(&ev,FLAG_KEY_RELEASE); } } } break; } } } if (motion_occured && (final_x!=parent->mouse_x || final_y!=parent->mouse_y)) { i4_mouse_move_event_class move(parent->mouse_x, parent->mouse_y, final_x, final_y); send_event_to_agents(&move,FLAG_MOUSE_MOVE); if (parent->mouse_locked) { XWarpPointer(parent->display, None, parent->mainwin, 0,0, 0,0, parent->mouse_x, parent->mouse_y); } else { parent->mouse_x=final_x; parent->mouse_y=final_y; } } return i4_F; } x11_display_class::x11_device_manager::x11_device_manager() { modifier_state=0; need_first_time=1; x11_start_time_best_diff=0; parent=&x11_display_instance; } x11_display_class::x11_display_class() { mouse_locked=i4_F; screen=0; context=0; shm_extension=0; shm_image=0; ximage=0; display=0; } i4_image_class *x11_display_class::get_screen() { return screen; } void db_show() { x11_display_instance.get_context()->add_both_dirty(0,0, x11_display_instance.get_screen()->width(), x11_display_instance.get_screen()->height()); x11_display_instance.flush(); x11_display_instance.get_context()->add_both_dirty(0,0, x11_display_instance.get_screen()->width(), x11_display_instance.get_screen()->height()); } static i4_profile_class x11_flush_ph("X11 video flush (XPutImage)"); void x11_display_class::flush() // the actual work of the function is in copy_part_to_vram { int mw,mh; i4_coord rmx,rmy; x11_flush_ph.start(); sw32 mx=mouse_x, my=mouse_y; if (mx<0) mx=0; if (my<0) my=0; rmx=mx-mouse_hot_x; rmy=my-mouse_hot_y; if (mouse_pict) { mw=mouse_pict->width(); mh=mouse_pict->height(); if (pal.source_type()==I4_8BIT) mouse_save->set_pal(pal); i4_draw_context_class save_context(0,0,mw-1,mh-1); screen->put_part(mouse_save,0,0,rmx,rmy,rmx+mw-1,rmy+mh-1, save_context); // save area behind mouse mouse_pict->put_image_trans(screen,rmx,rmy,mouse_trans,*context); } i4_rect_list_class::area_iter a; for (a=context->single_dirty->list.begin(); a!=context->single_dirty->list.end(); ++a) context->both_dirty->add_area(a->x1, a->y1, a->x2, a->y2); context->single_dirty->delete_list(); context->both_dirty->intersect_area(0,0,width()-1,height()-1); a=context->both_dirty->list.begin(); i4_bool error=i4_F; for (;a!=context->both_dirty->list.end();++a) error=(i4_bool)(error & copy_part_to_vram(a->x1,a->y1,a->x1,a->y1,a->x2,a->y2)); if (!error) context->both_dirty->delete_list(); if (mouse_pict) mouse_save->put_part(screen,rmx,rmy,0,0,mw-1,mh-1,*context); // restore area behind mouse x11_flush_ph.stop(); } // Dave : have the name return a string appropriate to the extensions // available on the Xserver, i.e. MITSHM or the page-flip thingy char *x11_display_class::name() const { char buf[200]; if (getenv("DISPLAY")) sprintf(buf,"X Server : %s",getenv("DISPLAY")); else strcpy(buf,"X Server : localhost"); return buf; } i4_display_class::mode *x11_display_class::get_first_mode() { // Check to see if we need to open the display, // since get_first_mode() is valid while a display is i4_bool need_close=i4_F; if (!display) { need_close=i4_T; if (!open_display()) return 0; } // Check to see if they have an 8 bit visual on the display int items; XVisualInfo vis_info; if (I4_SCREEN_DEPTH==8) { vis_info.depth=8; vis_info.c_class=PseudoColor; } else if (I4_SCREEN_DEPTH==15) { vis_info.depth=16; vis_info.c_class=TrueColor; } else if (I4_SCREEN_DEPTH==16) { vis_info.depth=16; vis_info.c_class=TrueColor; } else if (I4_SCREEN_DEPTH==32) { vis_info.depth=24; vis_info.c_class=TrueColor; } XVisualInfo *return_visual=XGetVisualInfo(display, VisualClassMask | VisualDepthMask, &vis_info,&items); amode.red_mask = return_visual->red_mask; amode.green_mask = return_visual->green_mask; amode.blue_mask = return_visual->blue_mask; XFree((char *)return_visual); if (items==0) { if (need_close) close_display(); return 0; } strcpy(amode.name,"Standard X11 window"); amode.flags=mode::RESOLUTION_DETERMINED_ON_OPEN; amode.bits_per_pixel=I4_SCREEN_DEPTH; Window root; int x,y; unsigned w,h,bord,depth; if (XGetGeometry(display,RootWindow(display,DefaultScreen(display)),&root, &x,&y,&w,&h,&bord,&depth)==0) i4_error("can't get root window attributes"); amode.xres=w; amode.yres=h; amode.assoc=this; // so we know in 'initialize_mode' that we created this mode if (need_close) close_display(); return &amode; } i4_display_class::mode *x11_display_class::get_next_mode(mode *last_mode) { return NULL; // for now only straight X stuff // this should return modes for direct screen access } void x11_display_class::close_X_window() { destroy_X_image(); if (shm_extension) shm_extension->shutdown(display); XFreeGC(display,gc); XFree((char *)my_visual); XCloseDisplay(display); display=0; if (I4_SCREEN_DEPTH==8) XFreeColormap(display,xcolor_map); if (mouse_save) { delete mouse_save; mouse_save=0; } if (mouse_pict) { delete mouse_pict; mouse_pict=0; } } void x11_display_class::destroy_X_image() { if (shm_extension) { if (shm_image) shm_extension->destroy_shm_image(display, shm_image); shm_image=0; } else { if (ximage) XDestroyImage(ximage); ximage=0; } if (screen) { delete screen; screen=0; } if (context) { delete context; context=0; } } void x11_display_class::create_X_image(w32 width, w32 height) { destroy_X_image(); if (shm_extension) { w16 w=width,h=height; shm_image=shm_extension->create_shm_image(display, mainwin, gc, my_visual->visual, my_visual->depth, w,h); // actual width and height from X screen=new I4_SCREEN_TYPE(w,h,pal, w*I4_BYTES_PER_PIXEL, (w8 *)shm_image->data); } else { ximage = XCreateImage( display, my_visual->visual, my_visual->depth, ZPixmap, 0, (char *)i4_malloc(width*height*I4_BYTES_PER_PIXEL,"Ximage"), width, height, 32, 0 ); screen=new I4_SCREEN_TYPE(width,height,pal, width*I4_BYTES_PER_PIXEL, // bytes per line (w8 *)ximage->data); } if (context) delete context; context=new i4_draw_context_class(0,0,screen->width()-1,screen->height()-1); context->both_dirty=new i4_rect_list_class; context->single_dirty=new i4_rect_list_class; } i4_bool x11_display_class::open_X_window(w32 width, w32 height, i4_display_class::mode *mode) // assumes that the display has already been grabbed { if (!open_display()) return i4_F; width=(width+3)&(~3); XVisualInfo vis_info; int items; XEvent report; XTextProperty winName; char *win_name,*icon_name; if (i4_global_argv) win_name=icon_name=i4_global_argv[0]; else win_name=icon_name="..."; // locate a visual same depth as i4_SCREEN_DEPTH if (I4_SCREEN_DEPTH==8) { vis_info.depth=8; vis_info.c_class=PseudoColor; } else if (I4_SCREEN_DEPTH==15) { vis_info.depth=16; vis_info.c_class=TrueColor; } else if (I4_SCREEN_DEPTH==16) { vis_info.depth=16; vis_info.c_class=TrueColor; } else if (I4_SCREEN_DEPTH==32) { vis_info.depth=24; vis_info.c_class=TrueColor; } my_visual = XGetVisualInfo(display,VisualClassMask | VisualDepthMask, &vis_info,&items); screen_num = DefaultScreen(display); if (items>0) { XMatchVisualInfo(display, screen_num, vis_info.depth, vis_info.c_class, &vis_info); my_visual->visual = vis_info.visual; } else return i4_F; Colormap tmpcmap; tmpcmap = XCreateColormap(display, XRootWindow(display,my_visual->screen),my_visual->visual, AllocNone); int attribmask = CWColormap | CWBorderPixel; XSetWindowAttributes attribs; attribs.border_pixel = 0; attribs.colormap = tmpcmap; width=(width+3)&(0xffffffff-3); mainwin=XCreateWindow(display, XRootWindow(display,my_visual->screen), 0,0, width,height, 0, my_visual->depth, InputOutput, my_visual->visual, attribmask, &attribs); XFreeColormap(display,tmpcmap); XSelectInput(display,mainwin, KeyPressMask | VisibilityChangeMask | ButtonPressMask | ButtonReleaseMask | ButtonMotionMask | PointerMotionMask | KeyReleaseMask | ExposureMask | StructureNotifyMask); XGCValues values; gc=XCreateGC(display,mainwin,0,&values); XSetBackground(display,gc,BlackPixel(display,screen_num)); XMapWindow(display,mainwin); do { XNextEvent(display, &report); } while (report.type!= Expose); // wait for our window to pop up /* if (I4_SCREEN_DEPTH==8) xcolor_map=XCreateColormap(display,mainwin,my_visual->visual,AllocAll); */ i4_pixel_format f; f.red_mask = mode->red_mask; f.green_mask = mode->green_mask; f.blue_mask = mode->blue_mask; f.alpha_mask = 0; f.calc_shift(); f.lookup = 0; f.pixel_depth = I4_16BIT; pal = i4_pal_man.register_pal(&f); if (shm_extension) { char *ds_name=getenv("DISPLAY"); if (!ds_name) ds_name="unix:0.0"; if (!shm_extension->available(display,ds_name)) shm_extension=0; } create_X_image(width, height); return i4_T; } i4_bool x11_display_class::initialize_mode(mode *which_one) { if ((((x11_mode *)which_one)->assoc)==this) { memcpy(&cur_mode,which_one,sizeof(cur_mode)); // this should open an X window open_X_window(cur_mode.xres, cur_mode.yres, which_one); XDefineCursor(display,mainwin, CreateNullCursor(display,mainwin)); i4_kernel.add_device(&devs); } else return i4_F; return i4_T; } i4_bool x11_display_class::close() { if (screen) { close_X_window(); i4_kernel.remove_device(&devs); } // close the X window, or return to normal mode of operation // prepare for end of program or next mode set } i4_bool x11_display_class::open_display() { // get the default display name from the environment variable DISPLAY char *ds_name=getenv("DISPLAY"); if (!ds_name || ds_name[0]==0) ds_name="unix:0.0"; // check the command line args for another display w32 i=1; for (;i >16)<<8)|0xff; color.green=((((*p)&0x0000ff00)>>8)<<8)|0xff; color.blue= ((((*p)&0x000000ff))<<8)|0xff; XStoreColor(display,xcolor_map,&color); } XSetWindowColormap(display,mainwin,xcolor_map); pal=pal_id; if (screen) screen->set_pal(pal); } */ return i4_T; } i4_bool x11_display_class::copy_part_to_vram(i4_coord x, i4_coord y, i4_coord x1, i4_coord y1, i4_coord x2, i4_coord y2) // this should copy a portion of an image to the actual screen at offset x, y { if (shm_image) return shm_image->copy_part_to_vram(shm_extension,x,y,x1,y1,x2,y2); else { XPutImage(display,mainwin,gc,ximage,x1,y1,x,y,x2-x1+1,y2-y1+1); return i4_T; } }