#ifdef HAVE_CONFIG_H #include #endif #include #include #include #include /* this includes the necessary X headers */ #include #include #include FT_FREETYPE_H #include "mem.h" #include "log.h" #include "back_end.h" #include "back_end_opengl_glx.h" struct glyph_data { struct ct_point offset; struct FT_Bitmap_ *bitmap; }; struct tr_string_data { int num_glyphs; struct glyph_data *glyphs; struct ct_size size; }; struct texture { GLuint texture; GLuint display_list; }; static void CHECK_ERROR(const char *msg) { GLenum error = glGetError(); if (error != GL_NO_ERROR) { freelog(LOG_ERROR, "got error 0x%x at '%s'", error,msg); assert(0); } } GLfloat xAngle = 0, yAngle = 0, zAngle = 0; static int num_lists=0; static GLuint *lists = NULL; static int dummy; static bool doubleBuffer; Display *display; int screen_number; int display_depth; Window window; static int x11fd; static int other_fd = -1; Colormap cmap; static void set_color(be_color color) { unsigned char red = ((color >> 16) & 0xff); unsigned char green = ((color >> 8) & 0xff); unsigned char blue = ((color) & 0xff); glColor3ub(red, green, blue); } be_color be_get_color(int red, int green, int blue) { assert(red >= 0 && red <= 255); assert(green >= 0 && green <= 255); assert(blue >= 0 && blue <= 255); return red << 16 | green << 8 | blue; } static int next_p2(int a) { int rval = 1; while (rval < a) { rval *=2; } return rval; } struct osda *be_create_osda(int width, int height) { return (struct osda *)&dummy; } void be_free_osda(struct osda *osda) { } void be_set_transparent(struct osda *osda, const struct ct_rect *rect) { } static struct texture *alpha_bitmap2texture(struct FT_Bitmap_ *bitmap, be_color color) { struct texture *result=fc_malloc(sizeof(*result)); int width = next_p2(bitmap->width); int height = next_p2(bitmap->rows); int i,j; GLubyte *expanded_data = fc_malloc(sizeof(GLubyte) * 2 * width * height); CHECK_ERROR("alpha_bitmap2texture: start"); glGenTextures(1, &result->texture); for (j = 0; j < height; j++) { for (i = 0; i < width; i++) { GLubyte value = 0; if (i < bitmap->width && j < bitmap->rows) { value = bitmap->buffer[i + bitmap->width * j]; } expanded_data[2 * (i + j * width)] = value; expanded_data[2 * (i + j * width) + 1] = value; } } CHECK_ERROR("alpha_bitmap2texture: #1"); glBindTexture(GL_TEXTURE_2D, result->texture); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, expanded_data); free(expanded_data); result->display_list = glGenLists(1); CHECK_ERROR("alpha_bitmap2texture: #2"); glNewList(result->display_list, GL_COMPILE); CHECK_ERROR("alpha_bitmap2texture: #4"); glBindTexture(GL_TEXTURE_2D, result->texture); CHECK_ERROR("alpha_bitmap2texture: #5"); glBegin(GL_QUADS); set_color(color); glTexCoord2d(0, 0); glVertex3f(0, 0, 0); glTexCoord2d(0, 1); glVertex3f(0, height, 0); glTexCoord2d(1, 1); glVertex3f(width, height, 0); glTexCoord2d(1, 0); glVertex3f(width, 0, 0); glEnd(); glEndList(); CHECK_ERROR("alpha_bitmap2texture:end"); return result; } static struct texture *mono_bitmap2texture(struct FT_Bitmap_ *bitmap, be_color color) { struct texture *result=fc_malloc(sizeof(*result)); int width = next_p2(bitmap->width); int height = next_p2(bitmap->rows); int i,j; GLubyte *expanded_data = fc_malloc(sizeof(GLubyte) * 2* width * height); CHECK_ERROR("alpha_bitmap2texture: start"); glGenTextures(1, &result->texture); for (j = 0; j < height; j++) { for (i = 0; i < width; i++) { GLubyte value = 0; if (i < bitmap->width && j < bitmap->rows) { unsigned char bv = bitmap->buffer[i/8 + bitmap->pitch * j]; value = TEST_BIT(bv, 7-(i % 8)) ? 255 : 0; } expanded_data[2 * (i + j * width)] = value; expanded_data[2 * (i + j * width) + 1] = value; } } CHECK_ERROR("mono_bitmap2texture: #1"); glBindTexture(GL_TEXTURE_2D, result->texture); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); CHECK_ERROR("mono_bitmap2texture: #1.5"); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, expanded_data); CHECK_ERROR("mono_bitmap2texture: #1.6"); free(expanded_data); result->display_list = glGenLists(1); CHECK_ERROR("mono_bitmap2texture: #2"); glNewList(result->display_list, GL_COMPILE); CHECK_ERROR("alpha_bitmap2texture: #4"); glBindTexture(GL_TEXTURE_2D, result->texture); CHECK_ERROR("alpha_bitmap2texture: #5"); glBegin(GL_QUADS); set_color(color); glTexCoord2d(0, 0); glVertex3f(0, 0, 0); glTexCoord2d(0, 1); glVertex3f(0, height, 0); glTexCoord2d(1, 1); glVertex3f(width, height, 0); glTexCoord2d(1, 0); glVertex3f(width, 0, 0); glEnd(); glEndList(); CHECK_ERROR("alpha_bitmap2texture:end"); return result; } void be_draw_bitmap(struct osda *target, enum be_draw_type draw_type, be_color color, const struct ct_point *position, struct FT_Bitmap_ *bitmap) { static float depth=0.0; struct texture *texture=NULL; if (bitmap->pixel_mode == ft_pixel_mode_mono) { texture = mono_bitmap2texture(bitmap,color); } else if (bitmap->pixel_mode == ft_pixel_mode_grays) { assert(bitmap->num_grays == 256); texture = alpha_bitmap2texture(bitmap,color); }else{ assert(0); } CHECK_ERROR("draw bitmap start"); printf("drawing %dx%d at (%d,%d)\n", bitmap->width, bitmap->rows, position->x, position->y); { int display_list = glGenLists(1); glNewList(display_list, GL_COMPILE); glMatrixMode(GL_MODELVIEW); glPushMatrix(); glTranslatef(position->x, position->y, depth); glCallList(texture->display_list); glPopMatrix(); glEndList(); num_lists++; lists = fc_realloc(lists, sizeof(*lists) * num_lists); lists[num_lists - 1] = display_list; } depth+=0.1; } void be_draw_region(struct osda *target, enum be_draw_type draw_type, const struct ct_rect *region, be_color color) { } void be_draw_line(struct osda *target, enum be_draw_type draw_type, const struct ct_point *start, const struct ct_point *end, int line_width, bool dashed, be_color color) { } void be_draw_rectangle(struct osda *target, enum be_draw_type draw_type, const struct ct_rect *spec, int line_width, be_color color) { } void be_draw_sprite(struct osda *target, enum be_draw_type draw_type, const struct Sprite *sprite, const struct ct_size *size, const struct ct_point *dest_pos, const struct ct_point *src_pos) { } void be_copy_osda_to_osda(struct osda *dest, struct osda *src, const struct ct_size *size, const struct ct_point *dest_pos, const struct ct_point *src_pos, int transparency) { } /* ===== query info ===== */ void be_screen_get_size(struct ct_size *size) { XWindowAttributes window_attributes; XGetWindowAttributes(display, window, &window_attributes); size->width = window_attributes.width; size->height = window_attributes.height; } #define be_string_get_size tr_string_get_size void be_sprite_get_size(struct ct_size *size, const struct Sprite *sprite) { size->width=4; size->height=4; } bool be_is_transparent_pixel(struct osda *osda, const struct ct_point *pos) { return FALSE; } /* ===== graphics.c implementation ===== */ struct Sprite *be_load_gfxfile(const char *filename) { return (struct Sprite *)&dummy; } struct Sprite *be_crop_sprite(struct Sprite *source, int x, int y, int width, int height) { return (struct Sprite *)&dummy; } /* ===== other ===== */ void be_init(void) { XVisualInfo *vi; GLXContext cx; _Xdebug = 0; display = XOpenDisplay(NULL); assert(display); x11fd = ConnectionNumber(display); { int dummy; if (!glXQueryExtension(display, &dummy, &dummy)) { freelog(LOG_ERROR, "X server has no OpenGL GLX extension"); assert(0); } } /* find an OpenGL-capable RGB visual with depth buffer */ { int snglBuf[] = { GLX_RGBA, GLX_DEPTH_SIZE, 16, None }; int dblBuf[] = { GLX_RGBA, GLX_DEPTH_SIZE, 16, GLX_DOUBLEBUFFER, None }; vi = glXChooseVisual(display, DefaultScreen(display), dblBuf); if (!vi) { vi = glXChooseVisual(display, DefaultScreen(display), snglBuf); if (!vi) { freelog(LOG_ERROR, "no RGB visual with depth buffer"); assert(0); } doubleBuffer = FALSE; } else { doubleBuffer = TRUE; } if (vi->class != TrueColor){ freelog(LOG_ERROR, "TrueColor visual required for this program"); } } screen_number = vi->screen; display_depth = DefaultDepth(display, screen_number); /* create an OpenGL rendering context */ cx = glXCreateContext(display, vi, NULL, True); assert(cx); cmap = XCreateColormap(display, RootWindow(display, vi->screen), vi->visual, AllocNone); { XSetWindowAttributes swa; swa.colormap = cmap; swa.border_pixel = 0; swa.background_pixel = WhitePixel(display, screen_number); swa.event_mask = ExposureMask | PointerMotionMask | ButtonPressMask | ButtonReleaseMask | KeyPressMask; window = XCreateWindow(display, RootWindow(display, vi->screen), 0, 0, 640, 480, 0, vi->depth, InputOutput, vi->visual, CWBackPixel | CWBorderPixel | CWColormap | CWEventMask, &swa); } /* XSetStandardProperties(display, win, "glxsimple", "glxsimple", None, argv, argc, NULL); */ glXMakeCurrent(display, window, cx); XMapWindow(display, window); glEnable(GL_DEPTH_TEST); /* enable depth buffering */ glEnable(GL_TEXTURE_2D); glEnable(GL_BLEND); glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glDepthFunc(GL_LESS); /* pedantic, GL_LESS is the default */ glClearDepth(1.0); /* pedantic, 1.0 is the default */ /* frame buffer clears should be to black */ glClearColor(0.5,0.5,0.5,0); //glClearColor(1,1,1,0); //glClearColor(0,0,0,0); /* establish initial viewport */ glViewport(0, 0, 640, 480); /* pedantic, full window size is default viewport */ /* set up projection transform */ glMatrixMode(GL_PROJECTION); glLoadIdentity(); //glFrustum(-1.0, 1.0, -1.0, 1.0, 1.0, 10.0); //glOrtho(0, 640, 480, 0, -1, 10); glOrtho(0, 640, 480, 0, -1, 10); for(;;) { struct be_event event; be_next_event(&event,NULL); if(event.type==BE_KEY_PRESSED && event.key_type == BE_KEY_NORMAL) { if(event.key=='a') { xAngle+=10; } if(event.key=='A') { xAngle-=10; } if(event.key=='s') { yAngle+=10; } if(event.key=='S') { yAngle-=10; } if(event.key=='d') { zAngle+=10; } if(event.key=='D') { zAngle-=10; } if(event.key=='q') { exit(0); } } } } static bool copy_event(struct be_event *event, XEvent * xevent) { switch (xevent->type) { case NoExpose: case GraphicsExpose: return FALSE; case Expose: { XExposeEvent *xev = &xevent->xexpose; if (xev->count > 0) { return FALSE; } event->type = BE_EXPOSE; } break; case MotionNotify: { XMotionEvent *xev = &xevent->xmotion; event->type = BE_MOUSE_MOTION; event->position.x = xev->x; event->position.y = xev->y; } break; case ButtonPress: case ButtonRelease: { XButtonEvent *xev = &xevent->xbutton; printf("pos=(%d,%d) button=0x%x state=0x%x\n", xev->x, xev->y, xev->button, xev->state); event->type = xevent->type == ButtonPress ? BE_MOUSE_PRESSED : BE_MOUSE_RELEASED; event->position.x = xev->x; event->position.y = xev->y; switch (xev->button) { case 1: event->button = BE_MB_LEFT; break; case 2: event->button = BE_MB_MIDDLE; break; case 3: event->button = BE_MB_RIGHT; break; default: assert(0); } } break; case KeyPress: { XKeyEvent *xev = &xevent->xkey; char string[10]; KeySym key; int chars = XLookupString(xev, string, sizeof(string), &key, NULL); if(0) printf("chars=%d string='%s' key=%ld cursor=%d\n", chars, string, key, IsCursorKey(key)); if (key == XK_BackSpace) { event->key_type = BE_KEY_BACKSPACE; } else if (key == XK_Return) { event->key_type = BE_KEY_RETURN; } else if (key == XK_KP_Enter) { event->key_type = BE_KEY_ENTER; } else if (key == XK_Delete) { event->key_type = BE_KEY_DELETE; } else if (key == XK_Left) { event->key_type = BE_KEY_LEFT; } else if (key == XK_Right) { event->key_type = BE_KEY_RIGHT; } else if (key == XK_Escape) { event->key_type = BE_KEY_ESCAPE; } else if (chars == 1 && string[0] >= ' ' && string[0] <= '~') { event->key_type = BE_KEY_NORMAL; event->key = string[0]; } else { printf ("WARNING: BE-X11: unconverted KeyPress: chars=%d string='%s' key=%ld\n", chars, string, key); return FALSE; } event->type = BE_KEY_PRESSED; event->position.x = xev->x; event->position.y = xev->y; } break; default: printf("got event %d\n", xevent->type); assert(0); } return TRUE; } static void redraw(void) { static GLboolean displayListInited = GL_FALSE; int i; CHECK_ERROR("redraw: start"); if (!displayListInited) { glNewList(1, GL_COMPILE); glColor3f(1, 1, 1); glBegin(GL_LINES); glVertex3f(0, 0, 1.0); glVertex3f(640, 480, 1.0); glVertex3f(0, 480, 1.0); glVertex3f(640, 0, 1.0); glEnd(); glEndList(); { struct ct_string *string = ct_string_create("data/fonts/verdana.ttf", 35, be_get_color(255, 255, 0), be_get_color(0, 0, 0), "no anti-alias", FALSE, 0, be_get_color(0, 0, 0)); struct ct_point position = { 100, 100 }; tr_draw_string(NULL, 0,&position,string); } { struct ct_string *string = ct_string_create("data/fonts/verdana.ttf", 35, be_get_color(255, 255, 0), be_get_color(0, 0, 0), "anti-alias", TRUE, 0, be_get_color(0, 0, 0)); struct ct_point position = { 100, 200 }; tr_draw_string(NULL, 0,&position,string); } displayListInited = GL_TRUE; CHECK_ERROR("redraw: end prepare"); } glMatrixMode(GL_MODELVIEW); /* reset modelview matrix to the identity matrix */ glLoadIdentity(); /* move the camera back three units */ glTranslatef(0.0, 0.0, -3.0); if(1){ /* rotate by X, Y, and Z angles */ glTranslatef(0, 200,0); glRotatef(xAngle, 0.1, 0.0, 0.0); glTranslatef(0, -200,0); glRotatef(yAngle, 0.0, 0.1, 0.0); glTranslatef(320, 200,0); glRotatef(zAngle, 0.0, 0.0, 1.0); glTranslatef(-320, -200,0); } glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glCallList(1); for (i = 0; i < num_lists; i++) { glCallList(lists[i]); } if (doubleBuffer) /* buffer swap does implicit glFlush */ glXSwapBuffers(display, window); else /* explicit flush for single buffered case */ glFlush(); CHECK_ERROR("redraw: end"); } void be_next_event(struct be_event *event, struct timeval *timeout) { XEvent xevent; restart: event->type = BE_NO_EVENT; if (XCheckMaskEvent(display, -1 /*all events */ , &xevent)) { //printf("got event %d\n", xevent.type); if (copy_event(event, &xevent)) { return; } else { /* discard event */ goto restart; } } //sw_paint_all(); redraw(); { fd_set readfds, exceptfds; int ret, highest = x11fd; /* No event available: block on input socket until one is */ FD_ZERO(&readfds); FD_SET(x11fd, &readfds); FD_ZERO(&exceptfds); if (other_fd != -1) { FD_SET(other_fd, &readfds); FD_SET(other_fd, &exceptfds); if (other_fd > highest) { highest = other_fd; } } ret = select(highest + 1, &readfds, NULL, &exceptfds, timeout); if (ret == 0) { // timed out event->type = BE_TIMEOUT; } else if (ret > 0) { if (other_fd != -1 && (FD_ISSET(other_fd, &readfds) || FD_ISSET(other_fd, &exceptfds))) { event->type = BE_DATA_OTHER_FD; event->socket = other_fd; return; } /* new data on the x11 fd */ goto restart; } else if (errno == EINTR) { goto restart; } else { assert(0); } } } void be_add_net_input(int sock) { } void be_remove_net_input(void) { } void be_copy_osda_to_screen(struct osda *src, const struct ct_size *size) { } void be_write_osda_to_file(struct osda *osda, const char *filename) { }