/* Provides a simple drawing context for X11/XWindows * You must have the X11 dev libraries installed. If missing, * 'sudo apt-get install libx11-dev' should help. */ #include // Every Xlib program must include this #include // needed for XGetPixel #include // needed for keyboard setup #include "x11context.h" #include "drawbase.h" #include /** * The only constructor provided. Allows size of window and background * color be specified. * */ X11Context::X11Context(unsigned int sizex=400,unsigned int sizey=400, unsigned int bg_color=GraphicsContext::BLACK) { // Open the display display = XOpenDisplay(NULL); // Holding a key in gives repeated key_press commands but only // one key_release int supported; XkbSetDetectableAutoRepeat(display,true,&supported); // Create a window - we will assume the color map is in RGB mode. window = XCreateSimpleWindow(display, DefaultRootWindow(display), 0, 0, sizex, sizey, 0, 0 , bg_color); // Sign up for MapNotify events XSelectInput(display, window, StructureNotifyMask); // Put the window on the screen XMapWindow(display, window); // Create a "Graphics Context" graphics_context = XCreateGC(display, window, 0, NULL); // Default color to white XSetForeground(display, graphics_context, GraphicsContext::WHITE); // Wait for MapNotify event for(;;) { XEvent e; XNextEvent(display, &e); if (e.type == MapNotify) break; } // We also want exposure, mouse, and keyboard events XSelectInput(display, window, ExposureMask| ButtonPressMask| ButtonReleaseMask| KeyPressMask| KeyReleaseMask| PointerMotionMask); // We need this to get the WM_DELETE_WINDOW message from the // window manager in case user click the X icon Atom atomKill = XInternAtom(display, "WM_DELETE_WINDOW", False); XSetWMProtocols(display, window, &atomKill, 1); return; } // Destructor - shut down window and connection to server X11Context::~X11Context() { XFreeGC(display, graphics_context); XDestroyWindow(display,window); XCloseDisplay(display); } // Set the drawing mode - argument is enumerated void X11Context::setMode(drawMode newMode) { if (newMode == GraphicsContext::MODE_NORMAL) { XSetFunction(display,graphics_context,GXcopy); } else { XSetFunction(display,graphics_context,GXxor); } } // Set drawing color - assume colormap is 24 bit RGB void X11Context::setColor(unsigned int color) { // Go ahead and set color here - better performance than setting // on every setPixel XSetForeground(display, graphics_context, color); } // Set a pixel in the current color void X11Context::setPixel(int x, int y) { XDrawPoint(display, window, graphics_context, x, y); XFlush(display); } unsigned int X11Context::getPixel(int x, int y) { XImage *image; image = XGetImage (display, window, x, y, 1, 1, AllPlanes, XYPixmap); XColor color; color.pixel = XGetPixel (image, 0, 0); XFree (image); XQueryColor (display, DefaultColormap(display, DefaultScreen (display)), &color); // I now have RGB values, but, they are 16 bits each, I only want 8-bits // each since I want a 24-bit RGB color value unsigned int pixcolor = color.red & 0xff00; pixcolor |= (color.green >> 8); pixcolor <<= 8; pixcolor |= (color.blue >> 8); return pixcolor; } void X11Context::clear() { XClearWindow(display, window); XFlush(display); } // Run event loop void X11Context::runLoop(DrawingBase* drawing) { run = true; while(run) { XEvent e; XNextEvent(display, &e); // Exposure event - lets not worry about region if (e.type == Expose) drawing->paint(this); // Key Down else if (e.type == KeyPress) drawing->keyDown(this,XLookupKeysym((XKeyEvent*)&e, (((e.xkey.state&0x01)&&!(e.xkey.state&0x02))|| (!(e.xkey.state&0x01)&&(e.xkey.state&0x02)))?1:0)); // Key Up else if (e.type == KeyRelease){ drawing->keyUp(this,XLookupKeysym((XKeyEvent*)&e, (((e.xkey.state&0x01)&&!(e.xkey.state&0x02))|| (!(e.xkey.state&0x01)&&(e.xkey.state&0x02)))?1:0)); } // Mouse Button Down else if (e.type == ButtonPress) drawing->mouseButtonDown(this, e.xbutton.button, e.xbutton.x, e.xbutton.y); // Mouse Button Up else if (e.type == ButtonRelease) drawing->mouseButtonUp(this, e.xbutton.button, e.xbutton.x, e.xbutton.y); // Mouse Move else if (e.type == MotionNotify) drawing->mouseMove(this, e.xmotion.x, e.xmotion.y); // This will respond to the WM_DELETE_WINDOW from the // window manager. else if (e.type == ClientMessage) break; } } int X11Context::getWindowWidth() { XWindowAttributes window_attributes; XGetWindowAttributes(display,window, &window_attributes); return window_attributes.width; } int X11Context::getWindowHeight() { XWindowAttributes window_attributes; XGetWindowAttributes(display,window, &window_attributes); return window_attributes.height; } // void X11Context::drawLine(int x1, int y1, int x2, int y2) // { // XDrawLine(display, window, graphics_context, x1, y1, x2, y2); // XFlush(display); // } // Bresenham Implementation void X11Context::drawLine(int x1, int y1, int x2, int y2){ //setPixel(x1,y1); // Check direction of line if(abs(y2-y1) < abs(x2-x1)){ if(x1 > x2){ //swap } else { //swap } } else { } int dx = x2-x1; int dy = y2-y1; int D = 2*dy-dx; int y = y1; for(int i = x1; i < x2; i++){ setPixel(i,y); if(D > 0){ y = y+1; D = D-2*dx; } D = D+2*dy; } } void X11Context::drawCircle(int x, int y, unsigned int radius) { XDrawArc(display, window, graphics_context, x-radius, y-radius, radius*2, radius*2, 0, 360*64); XFlush(display); }