Exemple #1
0
struct rtb_window *
window_impl_open(struct rutabaga *rtb,
                 int w, int h, const char *title, intptr_t parent)
{
    struct xcb_rutabaga *xrtb = (void *) rtb;
    struct xrtb_window *self;

    Display *dpy;
    xcb_connection_t *xcb_conn;

    int default_screen;

    GLXFBConfig *fb_configs, fb_config;
    XVisualInfo *visual;
    int nfb_configs;

    uint32_t event_mask =
        XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_EXPOSURE |
        XCB_EVENT_MASK_VISIBILITY_CHANGE | XCB_EVENT_MASK_BUTTON_PRESS |
        XCB_EVENT_MASK_BUTTON_RELEASE | XCB_EVENT_MASK_POINTER_MOTION |
        XCB_EVENT_MASK_ENTER_WINDOW | XCB_EVENT_MASK_LEAVE_WINDOW |
        XCB_EVENT_MASK_KEY_PRESS | XCB_EVENT_MASK_KEY_RELEASE |
        XCB_EVENT_MASK_KEYMAP_STATE;
    uint32_t value_mask =
        XCB_CW_BORDER_PIXEL | XCB_CW_BACK_PIXMAP |
        XCB_CW_BIT_GRAVITY |
        XCB_CW_EVENT_MASK | XCB_CW_COLORMAP;
    uint32_t value_list[6];

    xcb_colormap_t colormap;
    xcb_void_cookie_t ck_window, ck_map;
    xcb_generic_error_t *err;

    assert(rtb);
    assert(h > 0);
    assert(w > 0);

    if (!(self = calloc(1, sizeof(*self))))
        goto err_malloc;

    self->xrtb = xrtb;

    dpy = xrtb->dpy;
    xcb_conn = xrtb->xcb_conn;

    default_screen = DefaultScreen(dpy);

    self->screen = find_xcb_screen(xcb_conn, default_screen);
    if (!self->screen) {
        ERR("couldn't find XCB screen\n");
        goto err_screen;
    }

    /**
     * gl configuration
     */

    fb_configs = glXGetFBConfigs(dpy, default_screen, &nfb_configs);
    if (!fb_configs || !nfb_configs) {
        ERR("no GL configurations, bailing out\n");
        goto err_gl_config;
    }

    fb_config = find_reasonable_fb_config(dpy, xcb_conn, fb_configs,
                                          nfb_configs, 0);

    if (!fb_config) {
        ERR("no reasonable GL configurations, bailing out\n");
        goto err_gl_config;
    }

    visual = glXGetVisualFromFBConfig(dpy, fb_config);

    self->gl_ctx = new_gl_context(dpy, fb_config);
    if (!self->gl_ctx) {
        ERR("couldn't create GLX context\n");
        goto err_gl_ctx;
    }

    /**
     * window setup
     */

    colormap = xcb_generate_id(xcb_conn);
    self->xcb_win = xcb_generate_id(xcb_conn);

    xcb_create_colormap(
        xcb_conn, XCB_COLORMAP_ALLOC_NONE, colormap,
        self->screen->root, visual->visualid);

    value_list[0] = 0;
    value_list[1] = 0;
    value_list[2] = XCB_GRAVITY_STATIC;
    value_list[3] = event_mask;
    value_list[4] = colormap;
    value_list[5] = 0;

    ck_window = xcb_create_window_checked(
                    xcb_conn, visual->depth, self->xcb_win,
                    parent ? (xcb_window_t) parent : self->screen->root,
                    0, 0,
                    w, h,
                    0,
                    XCB_WINDOW_CLASS_INPUT_OUTPUT,
                    visual->visualid, value_mask, value_list);

    free(visual);

    if ((err = xcb_request_check(xcb_conn, ck_window))) {
        ERR("can't create XCB window: %d\n", err->error_code);
        goto err_xcb_win;
    }

    get_dpi(dpy, default_screen, &self->dpi.x, &self->dpi.y);

    self->gl_win = glXCreateWindow(dpy, fb_config, self->xcb_win, 0);
    if (!self->gl_win) {
        ERR("couldn't create GL window\n");
        goto err_gl_win;
    }

    if (set_xprop(xcb_conn, self->xcb_win, XCB_ATOM_WM_NAME, title))
        set_xprop(xcb_conn, self->xcb_win, XCB_ATOM_WM_NAME, "oh no");

    self->gl_draw = self->gl_win;

    if (!glXMakeContextCurrent(
                dpy, self->gl_draw, self->gl_draw, self->gl_ctx)) {
        ERR("couldn't activate GLX context\n");
        goto err_gl_make_current;
    }

    ck_map = xcb_map_window_checked(xcb_conn, self->xcb_win);
    if ((err = xcb_request_check(xcb_conn, ck_map))) {
        ERR("can't map XCB window: %d\n", err->error_code);
        goto err_win_map;
    }

    if (parent)
        raise_window(xcb_conn, self->xcb_win);
    else
        xcb_icccm_set_wm_protocols(xcb_conn,
                                   self->xcb_win, xrtb->atoms.wm_protocols,
                                   1, &xrtb->atoms.wm_delete_window);

    free(fb_configs);

    uv_mutex_init(&self->lock);
    return RTB_WINDOW(self);

err_win_map:
err_gl_make_current:
err_gl_win:
    xcb_destroy_window(xcb_conn, self->xcb_win);

err_xcb_win:
    glXDestroyContext(dpy, self->gl_ctx);

err_gl_ctx:
err_gl_config:
    free(fb_configs);
err_screen:
    free(self);

err_malloc:
    return NULL;
}
Exemple #2
0
// === SetAsPanel() ===
void LXCB::SetAsPanel(WId win){
  if(DEBUG){ qDebug() << "XCB: SetAsPanel()"; }
  if(win==0){ return; }
  SetDisableWMActions(win); //also need to disable WM actions for this window
  //Disable Input focus (panel activation ruins task manager window detection routines)
  //  - Disable Input flag in WM_HINTS
  xcb_icccm_wm_hints_t hints;
  //qDebug() << " - Disable WM_HINTS input flag";
  xcb_get_property_cookie_t cookie = xcb_icccm_get_wm_hints_unchecked(QX11Info::connection(), win);
  //qDebug() << " -- got cookie";
  if(1 == xcb_icccm_get_wm_hints_reply(QX11Info::connection(), cookie, &hints, NULL) ){
    //qDebug() << " -- Set no inputs flag";
     xcb_icccm_wm_hints_set_input(&hints, false); //set no input focus
     xcb_icccm_set_wm_hints(QX11Info::connection(), win, &hints); //save hints back to window
  }
  //  - Remove WM_TAKE_FOCUS from the WM_PROTOCOLS for the window
  //  - - Generate the necessary atoms
  //qDebug() << " - Generate WM_PROTOCOLS and WM_TAKE_FOCUS atoms";
  xcb_atom_t WM_PROTOCOLS, WM_TAKE_FOCUS; //the two atoms needed
  xcb_intern_atom_reply_t *preply = xcb_intern_atom_reply(QX11Info::connection(), \
			xcb_intern_atom(QX11Info::connection(), 0, 12, "WM_PROTOCOLS"), NULL);
  xcb_intern_atom_reply_t *freply = xcb_intern_atom_reply(QX11Info::connection(), \
			xcb_intern_atom(QX11Info::connection(), 0, 13, "WM_TAKE_FOCUS"), NULL); 
  bool gotatoms = false;
  if(preply && freply){
    WM_PROTOCOLS = preply->atom;
    WM_TAKE_FOCUS = freply->atom;
    free(preply);
    free(freply);
    gotatoms = true;
    //qDebug() << " -- success";
  }
  //  - - Now update the protocols for the window
  if(gotatoms){ //requires the atoms
    //qDebug() << " - Get WM_PROTOCOLS";
    xcb_icccm_get_wm_protocols_reply_t proto;
    if( 1 == xcb_icccm_get_wm_protocols_reply(QX11Info::connection(), \
			xcb_icccm_get_wm_protocols_unchecked(QX11Info::connection(), win, WM_PROTOCOLS), \
			&proto, NULL) ){
	
      //Found the current protocols, see if it has the focus atom set
			//remove the take focus atom and re-save them
      bool needremove = false;
      //Note: This first loop is required so that we can initialize the modified list with a valid size
      //qDebug() << " -- Check current protocols";
      for(unsigned int i=0; i<proto.atoms_len; i++){
        if(proto.atoms[i] == WM_TAKE_FOCUS){ needremove = true; break;}
      }
      if(needremove){
	//qDebug() << " -- Remove WM_TAKE_FOCUS protocol";
	xcb_atom_t *protolist = new xcb_atom_t[proto.atoms_len-1];
	int num = 0;
	for(unsigned int i=0; i<proto.atoms_len; i++){
	  if(proto.atoms[i] != WM_TAKE_FOCUS){
	    protolist[num] = proto.atoms[i];
	    num++;
	  }
	}
	//qDebug() << " -- Re-save modified protocols";
	xcb_icccm_set_wm_protocols(QX11Info::connection(), win, WM_PROTOCOLS, num, protolist);
      }
      //qDebug() << " -- Clear protocols reply";
      xcb_icccm_get_wm_protocols_reply_wipe(&proto);
    }//end of get protocols check
  } //end of gotatoms check
  //Make sure it has the "dock" window type
  //  - get the current window types (Not necessary, only 1 type of window needed)
  
  //  - set the adjusted window type(s)
  //qDebug() << " - Adjust window type";
  xcb_atom_t list[1]; 
    list[0] = EWMH._NET_WM_WINDOW_TYPE_DOCK;
  xcb_ewmh_set_wm_window_type(&EWMH, win, 1, list);
  
  //Make sure it is on all workspaces
  //qDebug() << " - Set window as sticky";
  SetAsSticky(win);
	
}
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);
}