void xcbosd_expose(xcbosd *osd) { assert (osd); lprintf("expose (state:%d)\n", osd->clean ); switch (osd->mode) { case XCBOSD_SHAPED: xcb_shape_mask(osd->connection, XCB_SHAPE_SO_SET, XCB_SHAPE_SK_BOUNDING, osd->u.shaped.window, 0, 0, osd->u.shaped.mask_bitmap); if( osd->clean==DRAWN ) { if( !osd->u.shaped.mapped ) { unsigned int stack_mode = XCB_STACK_MODE_ABOVE; xcb_configure_window(osd->connection, osd->u.shaped.window, XCB_CONFIG_WINDOW_STACK_MODE, &stack_mode); xcb_map_window(osd->connection, osd->u.shaped.window); } osd->u.shaped.mapped = 1; xcb_copy_area(osd->connection, osd->bitmap, osd->u.shaped.window, osd->gc, 0, 0, 0, 0, osd->width, osd->height); } else { if( osd->u.shaped.mapped ) xcb_unmap_window(osd->connection, osd->u.shaped.window); osd->u.shaped.mapped = 0; } break; case XCBOSD_COLORKEY: if( osd->clean!=UNDEFINED ) xcb_copy_area(osd->connection, osd->bitmap, osd->window, osd->gc, 0, 0, 0, 0, osd->width, osd->height); } }
static EGLBoolean dri2_copy_buffers(_EGLDriver *drv, _EGLDisplay *disp, _EGLSurface *surf, EGLNativePixmapType native_target) { struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); struct dri2_egl_surface *dri2_surf = dri2_egl_surface(surf); xcb_gcontext_t gc; xcb_pixmap_t target = (uintptr_t )native_target; (void) drv; (*dri2_dpy->flush->flush)(dri2_surf->dri_drawable); gc = xcb_generate_id(dri2_dpy->conn); xcb_create_gc(dri2_dpy->conn, gc, target, 0, NULL); xcb_copy_area(dri2_dpy->conn, dri2_surf->drawable, target, gc, 0, 0, 0, 0, dri2_surf->base.Width, dri2_surf->base.Height); xcb_free_gc(dri2_dpy->conn, gc); return EGL_TRUE; }
/** Refresh the window content by copying its pixmap data to its window. * \param sw The simple window to refresh. */ void simplewindow_refresh_pixmap_partial(simple_window_t *sw, int16_t x, int16_t y, uint16_t w, uint16_t h) { xcb_copy_area(globalconf.connection, sw->pixmap, sw->window, sw->gc, x, y, x, y, w, h); }
/** Refresh the window content by copying its pixmap data to its window. * \param drawin The drawin to refresh. * \param x The copy starting point x component. * \param y The copy starting point y component. * \param w The copy width from the x component. * \param h The copy height from the y component. */ void drawin_refresh_pixmap_partial(drawin_t *drawin, int16_t x, int16_t y, uint16_t w, uint16_t h) { if (!drawin->drawable || !drawin->drawable->pixmap || !drawin->drawable->refreshed) return; /* Make cairo do all pending drawing */ cairo_surface_flush(drawin->drawable->surface); xcb_copy_area(globalconf.connection, drawin->drawable->pixmap, drawin->window, globalconf.gc, x, y, x, y, w, h); }
void _cairo_xcb_connection_copy_area (cairo_xcb_connection_t *connection, xcb_drawable_t src, xcb_drawable_t dst, xcb_gcontext_t gc, int16_t src_x, int16_t src_y, int16_t dst_x, int16_t dst_y, uint16_t width, uint16_t height) { xcb_copy_area (connection->xcb_connection, src, dst, gc, src_x, src_y, dst_x, dst_y, width, height); }
/* Copy a rectangular part of the buffer into our out window. */ static void _swap_buffer(xcb_rectangle_t *clip_rect) { xcb_rectangle_t clip_r; if (clip_rect == NULL) { clip_r.x = clip_r.y = 0; clip_r.height = gd.def_screen->height_in_pixels; clip_r.width = gd.def_screen->width_in_pixels; } else { clip_r = *clip_rect; } xcb_copy_area(gd.conn, _this.win_buffer, _this.win, gd.default_gc, clip_r.x, clip_r.y, clip_r.x, clip_r.y, clip_r.width, clip_r.height); xcb_flush(gd.conn); }
/** * Pastes a rectangular area of the given pixmap onto the given drawable. * @param pmap The given pixmap. * @param dest The given drawable. * @param gc The graphics context which governs which operation will * be used to paste the area onto the drawable. * @param sx The X position of the area on the pixmap. * @param sy The Y position of the area on the pixmap. * @param w The width of the area. * @param h The height of the area. * @param dx The X position at which to paste the area on @p dest. * @param dy The Y position at which to paste the area on @p dest. * @ingroup Ecore_X_Pixmap_Group */ EAPI void ecore_x_pixmap_paste(Ecore_X_Pixmap pmap, Ecore_X_Drawable dest, Ecore_X_GC gc, int sx, int sy, int w, int h, int dx, int dy) { LOGFN(__FILE__, __LINE__, __FUNCTION__); CHECK_XCB_CONN; xcb_copy_area(_ecore_xcb_conn, pmap, dest, gc, sx, sy, dx, dy, w, h); // ecore_x_flush(); }
void event_loop(xcb_window_t window, xcb_gcontext_t graphics_context, xcb_pixmap_t pixmap) { xcb_generic_event_t *event; while(event = xcb_wait_for_event(connection)) { switch(event->response_type) { case XCB_EXPOSE: xcb_copy_area(connection, pixmap, window, graphics_context, 0, 0, 0, 0, neko_width, neko_height); xcb_flush(connection); break; case XCB_KEY_PRESS: return; } } }
/** Render a list of widgets. * \param wibox The wibox. * \todo Remove GC. */ void widget_render(wibox_t *wibox) { lua_State *L = globalconf.L; draw_context_t *ctx = &wibox->ctx; area_t rectangle = { 0, 0, 0, 0 }; color_t col; rectangle.width = ctx->width; rectangle.height = ctx->height; if (!widget_geometries(wibox)) return; if(ctx->bg.alpha != 0xffff) { int x = wibox->geometry.x + wibox->border_width, y = wibox->geometry.y + wibox->border_width; xcb_get_property_reply_t *prop_r; char *data; xcb_pixmap_t rootpix; xcb_get_property_cookie_t prop_c; xcb_screen_t *s = xutil_screen_get(globalconf.connection, ctx->phys_screen); prop_c = xcb_get_property_unchecked(globalconf.connection, false, s->root, _XROOTPMAP_ID, PIXMAP, 0, 1); if((prop_r = xcb_get_property_reply(globalconf.connection, prop_c, NULL))) { if(prop_r->value_len && (data = xcb_get_property_value(prop_r)) && (rootpix = *(xcb_pixmap_t *) data)) switch(wibox->orientation) { case North: draw_rotate(ctx, rootpix, ctx->pixmap, s->width_in_pixels, s->height_in_pixels, ctx->width, ctx->height, M_PI_2, y + ctx->width, - x); break; case South: draw_rotate(ctx, rootpix, ctx->pixmap, s->width_in_pixels, s->height_in_pixels, ctx->width, ctx->height, - M_PI_2, - y, x + ctx->height); break; case East: xcb_copy_area(globalconf.connection, rootpix, wibox->pixmap, wibox->gc, x, y, 0, 0, ctx->width, ctx->height); break; } p_delete(&prop_r); } } widget_node_array_t *widgets = &wibox->widgets; widget_node_array_wipe(widgets); widget_node_array_init(widgets); /* push wibox */ luaA_object_push(globalconf.L, wibox); /* push widgets table */ luaA_object_push_item(globalconf.L, -1, wibox->widgets_table); /* remove wibox */ lua_remove(globalconf.L, -2); luaA_table2widgets(L, widgets); /* get computed geometries */ for(unsigned int i = 0; i < lua_objlen(L, -1); i++) { lua_pushnumber(L, i + 1); lua_gettable(L, -2); widgets->tab[i].geometry.x = luaA_getopt_number(L, -1, "x", wibox->geometry.x); widgets->tab[i].geometry.y = luaA_getopt_number(L, -1, "y", wibox->geometry.y); widgets->tab[i].geometry.width = luaA_getopt_number(L, -1, "width", 1); widgets->tab[i].geometry.height = luaA_getopt_number(L, -1, "height", 1); lua_pop(L, 1); } lua_pop(L, 1); /* draw background image, only if the background color is not opaque */ if(wibox->bg_image && ctx->bg.alpha != 0xffff) draw_image(ctx, 0, 0, 1.0, wibox->bg_image); /* draw background color */ xcolor_to_color(&ctx->bg, &col); draw_rectangle(ctx, rectangle, 1.0, true, &col); /* draw everything! */ for(int i = 0; i < widgets->len; i++) if(widgets->tab[i].widget->isvisible) widgets->tab[i].widget->draw(widgets->tab[i].widget, ctx, widgets->tab[i].geometry, wibox); switch(wibox->orientation) { case South: draw_rotate(ctx, ctx->pixmap, wibox->pixmap, ctx->width, ctx->height, ctx->height, ctx->width, M_PI_2, ctx->height, 0); break; case North: draw_rotate(ctx, ctx->pixmap, wibox->pixmap, ctx->width, ctx->height, ctx->height, ctx->width, - M_PI_2, 0, ctx->width); break; case East: break; } }
/* * Handles expose events, that is, draws the window contents. * */ static int handle_expose() { /* re-draw the background */ xcb_rectangle_t border = {0, 0, 300, (15 * font.height) + 8}; xcb_change_gc(conn, pixmap_gc, XCB_GC_FOREGROUND, (uint32_t[]){ get_colorpixel("#000000") }); xcb_poly_fill_rectangle(conn, pixmap, pixmap_gc, 1, &border); set_font(&font); #define txt(x, row, text) \ draw_text_ascii(text, pixmap, pixmap_gc,\ x, (row - 1) * font.height + 4, 300 - x * 2) if (current_step == STEP_WELCOME) { /* restore font color */ set_font_colors(pixmap_gc, get_colorpixel("#FFFFFF"), get_colorpixel("#000000")); txt(10, 2, "You have not configured i3 yet."); txt(10, 3, "Do you want me to generate ~/.i3/config?"); txt(85, 5, "Yes, generate ~/.i3/config"); txt(85, 7, "No, I will use the defaults"); /* green */ set_font_colors(pixmap_gc, get_colorpixel("#00FF00"), get_colorpixel("#000000")); txt(25, 5, "<Enter>"); /* red */ set_font_colors(pixmap_gc, get_colorpixel("#FF0000"), get_colorpixel("#000000")); txt(31, 7, "<ESC>"); } if (current_step == STEP_GENERATE) { set_font_colors(pixmap_gc, get_colorpixel("#FFFFFF"), get_colorpixel("#000000")); txt(10, 2, "Please choose either:"); txt(85, 4, "Win as default modifier"); txt(85, 5, "Alt as default modifier"); txt(10, 7, "Afterwards, press"); txt(85, 9, "to write ~/.i3/config"); txt(85, 10, "to abort"); /* the not-selected modifier */ if (modifier == MOD_Mod4) txt(31, 5, "<Alt>"); else txt(31, 4, "<Win>"); /* the selected modifier */ set_font(&bold_font); set_font_colors(pixmap_gc, get_colorpixel("#FFFFFF"), get_colorpixel("#000000")); if (modifier == MOD_Mod4) txt(10, 4, "-> <Win>"); else txt(10, 5, "-> <Alt>"); /* green */ set_font(&font); set_font_colors(pixmap_gc, get_colorpixel("#00FF00"), get_colorpixel("#000000")); txt(25, 9, "<Enter>"); /* red */ set_font_colors(pixmap_gc, get_colorpixel("#FF0000"), get_colorpixel("#000000")); txt(31, 10, "<ESC>"); } /* Copy the contents of the pixmap to the real window */ xcb_copy_area(conn, pixmap, win, pixmap_gc, 0, 0, 0, 0, /* */ 500, 500); xcb_flush(conn); return 1; }
QPixmap QXcbScreen::grabWindow(WId window, int x, int y, int width, int height) const { if (width == 0 || height == 0) return QPixmap(); // TODO: handle multiple screens QXcbScreen *screen = const_cast<QXcbScreen *>(this); xcb_window_t root = screen->root(); if (window == 0) window = root; xcb_get_geometry_cookie_t geometry_cookie = xcb_get_geometry_unchecked(xcb_connection(), window); xcb_get_geometry_reply_t *reply = xcb_get_geometry_reply(xcb_connection(), geometry_cookie, NULL); if (!reply) { return QPixmap(); } if (width < 0) width = reply->width - x; if (height < 0) height = reply->height - y; geometry_cookie = xcb_get_geometry_unchecked(xcb_connection(), root); xcb_get_geometry_reply_t *root_reply = xcb_get_geometry_reply(xcb_connection(), geometry_cookie, NULL); if (!root_reply) { free(reply); return QPixmap(); } if (reply->depth == root_reply->depth) { // if the depth of the specified window and the root window are the // same, grab pixels from the root window (so that we get the any // overlapping windows and window manager frames) // map x and y to the root window xcb_translate_coordinates_cookie_t translate_cookie = xcb_translate_coordinates_unchecked(xcb_connection(), window, root, x, y); xcb_translate_coordinates_reply_t *translate_reply = xcb_translate_coordinates_reply(xcb_connection(), translate_cookie, NULL); if (!translate_reply) { free(reply); free(root_reply); return QPixmap(); } x = translate_reply->dst_x; y = translate_reply->dst_y; window = root; free(translate_reply); free(reply); reply = root_reply; } else { free(root_reply); root_reply = 0; } xcb_get_window_attributes_reply_t *attributes_reply = xcb_get_window_attributes_reply(xcb_connection(), xcb_get_window_attributes_unchecked(xcb_connection(), window), NULL); if (!attributes_reply) { free(reply); return QPixmap(); } const xcb_visualtype_t *visual = screen->visualForId(attributes_reply->visual); free(attributes_reply); xcb_pixmap_t pixmap = xcb_generate_id(xcb_connection()); xcb_create_pixmap(xcb_connection(), reply->depth, pixmap, window, width, height); uint32_t gc_value_mask = XCB_GC_SUBWINDOW_MODE; uint32_t gc_value_list[] = { XCB_SUBWINDOW_MODE_INCLUDE_INFERIORS }; xcb_gcontext_t gc = xcb_generate_id(xcb_connection()); xcb_create_gc(xcb_connection(), gc, pixmap, gc_value_mask, gc_value_list); xcb_copy_area(xcb_connection(), window, pixmap, gc, x, y, 0, 0, width, height); QPixmap result = qt_xcb_pixmapFromXPixmap(connection(), pixmap, width, height, reply->depth, visual); free(reply); xcb_free_gc(xcb_connection(), gc); xcb_free_pixmap(xcb_connection(), pixmap); return result; }
int main(int arg, char **argv) { srand(time(NULL)); xcb_connection_t *connection = xcb_connect(NULL, NULL); xcb_screen_t *screen = xcb_setup_roots_iterator(xcb_get_setup(connection)).data; xcb_colormap_t colormap = screen->default_colormap; xcb_drawable_t window = xcb_generate_id(connection); uint32_t mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK; uint32_t values[] = {screen->black_pixel, XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_BUTTON_PRESS}; xcb_create_window(connection, /*screen->root_depth,*/ 24, window, screen->root, 0, 0, WIDTH, HEIGHT, 1, XCB_WINDOW_CLASS_INPUT_OUTPUT, screen->root_visual, mask, values); xcb_pixmap_t pixmap = xcb_generate_id(connection); xcb_create_pixmap(connection, 24, pixmap, window, WIDTH, HEIGHT); uint8_t *img = malloc(WIDTH * HEIGHT * 4); uint8_t *limg = img; /*for (int y = 0; y < HEIGHT; y++)*/ /*for (int x = 0; x < WIDTH; x++) {*/ /**(limg++) = 128;*/ /**(limg++) = 128;*/ /**(limg++) = 128;*/ /*limg++;*/ /*}*/ perlin(img, WIDTH, HEIGHT); xcb_image_t *image = xcb_image_create(WIDTH, HEIGHT, XCB_IMAGE_FORMAT_Z_PIXMAP, 8, 24, 32, 0, /*xcb_get_setup(connection)->image_byte_order,*/ XCB_IMAGE_ORDER_MSB_FIRST, XCB_IMAGE_ORDER_LSB_FIRST, img, WIDTH * HEIGHT * 4, img); xcb_gcontext_t gc = xcb_generate_id(connection); xcb_create_gc(connection, gc, pixmap, 0, NULL); xcb_image_put(connection, pixmap, gc, image, 0, 0, 0); xif_write(image, "test.xif"); xcb_map_window(connection, window); xcb_flush(connection); xcb_generic_event_t *event; xcb_expose_event_t *expose; while ((event = xcb_wait_for_event(connection))) { switch (event->response_type & ~0x80) { case XCB_EXPOSE: expose = (xcb_expose_event_t *)event; xcb_copy_area(connection, pixmap, window, gc, expose->x, expose->y, expose->x, expose->y, expose->width, expose->height); xcb_flush(connection); break; case XCB_BUTTON_PRESS: goto end; break; default: break; } free(event); } end: xcb_free_pixmap(connection, pixmap); xcb_disconnect(connection); xcb_image_destroy(image); return 0; }
int main(int argc, char **argv) { xcb_connection_t *connection = xcb_connect(NULL, NULL); xcb_screen_t *screen = xcb_setup_roots_iterator(xcb_get_setup(connection)).data; uint32_t mask = XCB_CW_BACK_PIXEL; uint32_t values[] = {screen->black_pixel}; xcb_drawable_t window = xcb_generate_id(connection); xcb_create_window(connection, 24, window, screen->root, 0, 0, WIDTH, HEIGHT, 1, XCB_WINDOW_CLASS_INPUT_OUTPUT, screen->root_visual, mask, values); xcb_change_property(connection, XCB_PROP_MODE_REPLACE, window, XCB_ATOM_WM_NAME, XCB_ATOM_STRING, 8, strlen(argv[0]), argv[0]); xcb_pixmap_t pixmap = xcb_generate_id(connection); xcb_create_pixmap(connection, 24, pixmap, window, WIDTH, HEIGHT); uint8_t *img = malloc(WIDTH * HEIGHT * 4); xcb_image_t *image = xcb_image_create(WIDTH, HEIGHT, XCB_IMAGE_FORMAT_Z_PIXMAP, 8, 24, 32, 0, XCB_IMAGE_ORDER_MSB_FIRST, XCB_IMAGE_ORDER_LSB_FIRST, img, WIDTH * HEIGHT * 4, img); mask = XCB_GC_FOREGROUND | XCB_GC_BACKGROUND; values[0] = screen->black_pixel; values[1] = 0xFFFFFF; xcb_gcontext_t gc = xcb_generate_id(connection); xcb_create_gc(connection, gc, pixmap, mask, values); xcb_image_put(connection, pixmap, gc, image, 0, 0, 0); xcb_map_window(connection, window); xcb_flush(connection); uint8_t value = 0; uint32_t *limg; Gol *gol = gol_new(BOARD_WIDTH, BOARD_HEIGHT); gol_random(gol, time(NULL)); while (1) { limg = (uint32_t *)image->data; for (int i = 0; i < BOARD_WIDTH * BOARD_HEIGHT; i++) *(limg++) = gol->buffers[gol->front][i] ? 0x00FFFF00 : 0x00000000; xcb_image_put(connection, pixmap, gc, image, 0, 0, 0); xcb_copy_area(connection, pixmap, window, gc, 0, 0, 0, 0, WIDTH, HEIGHT); xcb_flush(connection); value++; gol_simulate(gol); } return 0; }
int main() { hhxcb_state state = {}; hhxcb_get_binary_name(&state); char source_game_code_library_path[HHXCB_STATE_FILE_NAME_LENGTH]; char *game_code_filename = (char *) _HHXCB_QUOTE(GAME_CODE_FILENAME); hhxcb_build_full_filename(&state, game_code_filename, sizeof(source_game_code_library_path), source_game_code_library_path); hhxcb_game_code game_code = {}; hhxcb_load_game(&game_code, source_game_code_library_path); hhxcb_context context = {}; /* Open the connection to the X server. Use the DISPLAY environment variable */ int screenNum; context.connection = xcb_connect (NULL, &screenNum); context.key_symbols = xcb_key_symbols_alloc(context.connection); /* * TODO(nbm): This is X-wide, so it really isn't a good option in reality. * We have to be careful and clean up at the end. If we crash, auto-repeat * is left off. */ { uint32_t values[1] = {XCB_AUTO_REPEAT_MODE_OFF}; xcb_change_keyboard_control(context.connection, XCB_KB_AUTO_REPEAT_MODE, values); } load_atoms(&context); context.setup = xcb_get_setup(context.connection); xcb_screen_iterator_t iter = xcb_setup_roots_iterator(context.setup); xcb_screen_t *screen = iter.data; context.fmt = hhxcb_find_format(&context, 32, 24, 32); int monitor_refresh_hz = 60; real32 game_update_hz = (monitor_refresh_hz / 2.0f); // Should almost always be an int... long target_nanoseconds_per_frame = (1000 * 1000 * 1000) / game_update_hz; uint32_t mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK; uint32_t values[2] = { 0x0000ffff, //screen->black_pixel, 0 | XCB_EVENT_MASK_POINTER_MOTION | XCB_EVENT_MASK_KEY_PRESS | XCB_EVENT_MASK_KEY_RELEASE , }; #define START_WIDTH 960 #define START_HEIGHT 540 context.window = xcb_generate_id(context.connection); // NOTE: changed to not have a border width, so the min/max/close // buttons align on compiz, maybe other window managers xcb_create_window(context.connection, XCB_COPY_FROM_PARENT, context.window, screen->root, 0, 0, START_WIDTH, START_HEIGHT, 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, screen->root_visual, mask, values); xcb_icccm_set_wm_name(context.connection, context.window, XCB_ATOM_STRING, 8, strlen("hello"), "hello"); load_and_set_cursor(&context); xcb_map_window(context.connection, context.window); xcb_atom_t protocols[] = { context.wm_delete_window, }; xcb_icccm_set_wm_protocols(context.connection, context.window, context.wm_protocols, 1, protocols); xcb_size_hints_t hints = {}; xcb_icccm_size_hints_set_max_size(&hints, START_WIDTH, START_HEIGHT); xcb_icccm_size_hints_set_min_size(&hints, START_WIDTH, START_HEIGHT); xcb_icccm_set_wm_size_hints(context.connection, context.window, XCB_ICCCM_WM_STATE_NORMAL, &hints); hhxcb_offscreen_buffer buffer = {}; hhxcb_resize_backbuffer(&context, &buffer, START_WIDTH, START_HEIGHT); xcb_flush(context.connection); hhxcb_sound_output sound_output = {}; sound_output.samples_per_second = 48000; sound_output.bytes_per_sample = sizeof(int16) * 2; sound_output.secondary_buffer_size = sound_output.samples_per_second * sound_output.bytes_per_sample; hhxcb_init_alsa(&context, &sound_output); int16 *sample_buffer = (int16 *)malloc(sound_output.secondary_buffer_size); thread_context t = {}; game_memory m = {}; m.PermanentStorageSize = 256 * 1024 * 1024; m.TransientStorageSize = 256 * 1024 * 1024; state.total_size = m.PermanentStorageSize + m.TransientStorageSize; state.game_memory_block = calloc(state.total_size, sizeof(uint8)); m.PermanentStorage = (uint8 *)state.game_memory_block; m.TransientStorage = (uint8_t *)m.PermanentStorage + m.TransientStorageSize; #ifdef HANDMADE_INTERNAL m.DEBUGPlatformFreeFileMemory = debug_xcb_free_file_memory; m.DEBUGPlatformReadEntireFile = debug_xcb_read_entire_file; m.DEBUGPlatformWriteEntireFile = debug_xcb_write_entire_file; #endif hhxcb_init_replays(&state); bool ending = 0; timespec last_counter = {}; timespec flip_wall_clock = {}; // Actually monotonic clock clock_gettime(HHXCB_CLOCK, &last_counter); clock_gettime(HHXCB_CLOCK, &flip_wall_clock); game_input input[2] = {}; game_input *new_input = &input[0]; game_input *old_input = &input[1]; int64_t next_controller_refresh = 0; while(!ending) { if (last_counter.tv_sec >= next_controller_refresh) { hhxcb_refresh_controllers(&context); next_controller_refresh = last_counter.tv_sec + 1; } struct stat library_statbuf = {}; stat(source_game_code_library_path, &library_statbuf); if (library_statbuf.st_mtime != game_code.library_mtime) { hhxcb_unload_game(&game_code); hhxcb_load_game(&game_code, source_game_code_library_path); } new_input->dtForFrame = target_nanoseconds_per_frame / (1024.0 * 1024 * 1024); hhxcb_process_events(&context, &state, new_input, old_input); if (context.ending_flag) { break; } // NOTE: setup game_buffer.Memory upside down and set // game_buffer.pitch negative, so the game would fill the // backbuffer upside down. XCB doesn't seem to have an // option to flip the image. game_offscreen_buffer game_buffer = {}; game_buffer.Memory = ((uint8*)buffer.xcb_image->data)+ (buffer.width*(buffer.height-1)*buffer.bytes_per_pixel); game_buffer.Width = buffer.width; game_buffer.Height = buffer.height; game_buffer.Pitch = -buffer.pitch; if (state.recording_index) { hhxcb_record_input(&state, new_input); } if (state.playback_index) { hhxcb_playback_input(&state, new_input); } if (game_code.UpdateAndRender) { game_code.UpdateAndRender(&t, &m, new_input, &game_buffer); HandleDebugCycleCounter(&m); } game_sound_output_buffer sound_buffer; sound_buffer.SamplesPerSecond = sound_output.samples_per_second; sound_buffer.SampleCount = sound_output.samples_per_second / 30; sound_buffer.Samples = sample_buffer; int err, frames; snd_pcm_sframes_t delay, avail; snd_pcm_avail_delay(context.handle, &avail, &delay); if (avail == sound_output.secondary_buffer_size) { // NOTE(nbm): Full available buffer, starting with ~60ms of silence bzero(sample_buffer, sound_buffer.SampleCount * sound_output.bytes_per_sample); snd_pcm_writei(context.handle, sample_buffer, sound_buffer.SampleCount); snd_pcm_writei(context.handle, sample_buffer, sound_buffer.SampleCount); snd_pcm_writei(context.handle, sample_buffer, sound_buffer.SampleCount); snd_pcm_writei(context.handle, sample_buffer, sound_buffer.SampleCount); } else { uint32 target_available_frames = sound_output.secondary_buffer_size; target_available_frames -= (sound_buffer.SampleCount * 1); if (avail - target_available_frames < sound_buffer.SampleCount) { sound_buffer.SampleCount += avail - target_available_frames; } } game_code.GetSoundSamples(&t, &m, &sound_buffer); if (sound_buffer.SampleCount > 0) { frames = snd_pcm_writei(context.handle, sample_buffer, sound_buffer.SampleCount); if (frames < 0) { frames = snd_pcm_recover(context.handle, frames, 0); } if (frames < 0) { printf("snd_pcm_writei failed: %s\n", snd_strerror(frames)); break; } if (frames > 0 && frames < sound_buffer.SampleCount) { printf("Short write (expected %i, wrote %i)\n", sound_buffer.SampleCount, frames); } } xcb_image_put(context.connection, buffer.xcb_pixmap_id, buffer.xcb_gcontext_id, buffer.xcb_image, 0, 0, 0); xcb_flush(context.connection); timespec target_counter = {}; target_counter.tv_sec = last_counter.tv_sec; target_counter.tv_nsec = last_counter.tv_nsec + target_nanoseconds_per_frame; if (target_counter.tv_nsec > (1000 * 1000 * 1000)) { target_counter.tv_sec++; target_counter.tv_nsec %= (1000 * 1000 * 1000); } timespec work_counter = {}; clock_gettime(HHXCB_CLOCK, &work_counter); bool32 might_need_sleep = 0; if (work_counter.tv_sec < target_counter.tv_sec) { might_need_sleep = 1; } else if ((work_counter.tv_sec == target_counter.tv_sec) && (work_counter.tv_nsec < target_counter.tv_nsec)) { might_need_sleep = 1; } if (might_need_sleep) { timespec sleep_counter = {}; sleep_counter.tv_nsec = target_counter.tv_nsec - work_counter.tv_nsec; if (sleep_counter.tv_nsec < 0) { sleep_counter.tv_nsec += (1000 * 1000 * 1000); } // To closest ms sleep_counter.tv_nsec -= sleep_counter.tv_nsec % (1000 * 1000); if (sleep_counter.tv_nsec > 0) { timespec remaining_sleep_counter = {}; nanosleep(&sleep_counter, &remaining_sleep_counter); } else { // TODO(nbm): Log missed sleep } } timespec spin_counter = {}; clock_gettime(HHXCB_CLOCK, &spin_counter); while (spin_counter.tv_sec <= target_counter.tv_sec && spin_counter.tv_nsec < target_counter.tv_nsec) { clock_gettime(HHXCB_CLOCK, &spin_counter); } timespec end_counter = {}; clock_gettime(HHXCB_CLOCK, &end_counter); long ns_per_frame = end_counter.tv_nsec - last_counter.tv_nsec; if (ns_per_frame < 0) { ns_per_frame += (1000 * 1000 * 1000) * (end_counter.tv_sec - last_counter.tv_sec); } last_counter = end_counter; real32 ms_per_frame = ns_per_frame / (1000 * 1000.0); xcb_copy_area(context.connection, buffer.xcb_pixmap_id, context.window, buffer.xcb_gcontext_id, 0,0, 0, 0, buffer.xcb_image->width, buffer.xcb_image->height); xcb_flush(context.connection); game_input *temp_input = new_input; new_input = old_input; old_input = temp_input; } snd_pcm_close(context.handle); // NOTE(nbm): Since auto-repeat seems to be a X-wide thing, let's be nice // and return it to where it was before? { uint32_t values[1] = {XCB_AUTO_REPEAT_MODE_DEFAULT}; xcb_change_keyboard_control(context.connection, XCB_KB_AUTO_REPEAT_MODE, values); } xcb_flush(context.connection); xcb_disconnect(context.connection); }
/* Main driver */ int main (int argc, char **argv) { xcb_connection_t *conn; int conn_screen; xcb_screen_t *root_screen; xcb_drawable_t root_window; xcb_connection_t *conn_two; int conn_two_screen; xcb_screen_t *root_two_screen; xcb_drawable_t root_two_window; xcb_drawable_t window; uint32_t mask; uint32_t values[1]; xcb_void_cookie_t cookie; xcb_get_geometry_reply_t *geom_reply; xcb_generic_event_t *event; image_data_t img_data; xcb_image_t *image; xcb_pixmap_t pixmap; xcb_gcontext_t gc; /* Check the first argument to see what display to connect to. If empty, then use default display. */ if (argc > 1) { conn = xcb_connect(argv[1], &conn_screen); } else { conn = xcb_connect(NULL, &conn_screen); } root_screen = xcb_aux_get_screen(conn, conn_screen); root_window = root_screen->root; /* Get the geometry of the root window */ geom_reply = GetWindowGeometry(conn, root_window); WriteWindowInfo(conn, root_window); WriteAllChildrenWindowInfo(conn, root_window); img_data = GetWindowImageData(conn, root_window); xcb_flush(conn); /* Get the image of the root window */ image = xcb_image_get(conn, root_window, geom_reply->x, geom_reply->y, geom_reply->width, geom_reply->height, (unsigned int) ~0L, XCB_IMAGE_FORMAT_Z_PIXMAP); /* Set up the events the window will recognize */ mask = XCB_CW_EVENT_MASK; values[0] = XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_KEY_PRESS; /* Create our new window on the default display. Make it half the size */ conn_two = xcb_connect(NULL, &conn_two_screen); root_two_screen = xcb_aux_get_screen(conn_two, conn_two_screen); root_two_window = root_two_screen->root; window = xcb_generate_id(conn_two); cookie = xcb_create_window_checked(conn_two, XCB_COPY_FROM_PARENT, window, root_two_window, geom_reply->x, geom_reply->y, geom_reply->width / 2, geom_reply->height / 2, geom_reply->border_width, XCB_WINDOW_CLASS_INPUT_OUTPUT, root_two_screen->root_visual, mask, values); if (RequestCheck(conn_two, cookie, "Falied to create new window")) { exit(1); } WriteWindowInfo(conn_two, window); /* Map the window and flush the connection so it draws to the screen */ xcb_map_window(conn_two, window); xcb_flush(conn_two); WriteWindowInfo(conn_two, window); /* Create the pixmap and associate it with our new window. */ pixmap = xcb_generate_id(conn_two); cookie = xcb_create_pixmap(conn_two, geom_reply->depth, pixmap, window, geom_reply->width, geom_reply->height); if (RequestCheck(conn_two, cookie, "Failed to create pixmap")) { exit(1); } /* Put the root_window image into the pixmap. Note that a gc is * created, but I believe it is ignored. */ gc = xcb_generate_id(conn_two); xcb_create_gc(conn_two, gc, window, 0, 0); cookie = xcb_image_put(conn_two, pixmap, gc, image, 0, 0, 0); if (RequestCheck(conn_two, cookie, "Failed to put image into pixmap")) { exit(1); } /* Copy the pixmap into the new window */ cookie = xcb_copy_area(conn_two, pixmap, window, gc, 0, 0, 0, 0, geom_reply->width / 2, geom_reply->height / 2); if (RequestCheck(conn_two, cookie, "Failed to put image into pixmap")) { exit(1); } xcb_flush(conn_two); WriteWindowInfo(conn_two, window); /* Enter infinte loop so the window stays open */ while (1) { } /* Never get here, but if we could, would still want to clean up memory */ free(geom_reply); xcb_disconnect(conn); xcb_disconnect(conn_two); return 0; }