Notes on X library - maintain local stack of error handlers - maintain local queue of expected replies - init() push_error_handler (default_error_handler) [we will probably need a push_error_handler_unlocked()] - iterate (ex_boolean may_block): LOCK(); if reply { look at expected replies if there, queue reply->func, display remove it queue decode() else complain } else if event { while next_expected_reply <= event.serial queue complaint and remove reply queue event(); } else if error { while (next_expected_reply < error.serial) queue complaint and remove reply if (next expected reply matches == error.serial) remove reply pop_obsolete_handlers(); for (hnd = top; hnd; hnd = hnd->next) { if (error.serial >= item.begin_serial) { queue hnd.handler (error); break; } } } else { queue_error ("garbage from server"); queue_close (""); } UNLOCK(); run queue(); /* note queue must be local / on stack */ Error handling: /* what about wraparound? */ struct ErrorHandlerItem { int begin_serial; int end_serial; ErrorHandler handler; }; push() { pop_obsolete_handlers(); item = new Item item.begin_serial = next_serial; item.end_serial = MAX_INT; item->next = current_handler; } pop() { if (!current_handler) { programmer_error ("pop without push"); return; } top_item.end_serial = next_serial; } - flushing is application's response /* if block is FALSE and this return FALSE, the application is * expected to call select on the fd and call flush again * when the fd selects writable */ display_flush (display, boolean block) { write() if (EAGAIN) { if (block) select (); write(); return TRUE; else return FALSE; } if (error) { queue_fatal_error(); return FALSE; } } - general stuff ex_[display?]_set_lock(func, data) ex_[display?]_set_unlock(func, data) if func's are non-NULL, they'll be called around critical regions ex_set_out_of_memory_func(); // can free stuff // and return TRUE -> operation // will be retried. ex_set_malloc() ex_set_free() - display methods ex_display_set_event_func () ex_display_set_connection_closed_func(); // X server closed connection ex_display_push_error_func () ex_display_pop_error_func () protocol code: header: typedef struct ExRenderPictureAttributes ExRenderPictureAttributes; struct ExRenderPictureAttributes { int repeat; ExRenderPicture * alpha_map; int alpha_x_origin; int alpha_y_origin; int clip_x_origin; int clip_y_origin; ExCorePixmap * clip_mask; int graphics_exposures; }; typedef enum { EX_RENDER_PICTURE_REPEAT = 1 << 0, EX_RENDER_PICTURE_ALPHA_MAP = 1 << 1, EX_RENDER_PICTURE_ALPHA_Y_ORIGIN = 1 << 2, EX_RENDER_PICTURE_ALPHA_X_ORIGIN = 1 << 3, EX_RENDER_PICTURE_CLIP_X_ORIGIN = 1 << 4, EX_RENDER_PICTURE_CLIP_Y_ORIGIN = 1 << 5, EX_RENDER_PICTURE_CLIP_MASK = 1 << 6, EX_RENDER_PICTURE_GRAPHICS_EXPOSURES = 1 << 7, EX_RENDER_PICTURE_SUBWINDOW_MODE = 1 << 8, EX_RENDER_PICTURE_POLY_EDGE = 1 << 9, EX_RENDER_PICTURE_POLY_MODE = 1 << 10, EX_RENDER_PICTURE_DITHER = 1 << 11, EX_RENDER_PICTURE_COMPONENT_ALPHA = 1 << 12 } ExRenderPictureValueMask; .c: static void query_version_reply (ExDisplay *display, ExRenderQueryVersionFunc func, ex_pointer data) { int major, minor; LOCK() ex_display_read_reply (display, "uu", &major, &minor); UNLOCK(); func (major, minor, data); } void ex_render_query_version (ExDisplay *display, int major, int minor, ExRenderQueryVersionFunc func, ex_pointer data) { LOCK(); ex_display_send_request (display, "uu", major, minor); ex_display_expect_reply (display, query_version_reply, func, data); UNLOCK(); } /* CreatePicture */ ExRenderPicture * ex_render_create_picture (ExDisplay * display, ExCoreDrawable * drawable, ExRenderPictFormat * pict_format, ExRenderPictureAttributes * attributes, ExRenderPictureValueMask mask); { ExRenderPicture *picture; LOCK(); picture = ex_display_new(); ex_display_send_request (display, "XXXB(bXssssXbbbb)", drawable, pict_format, mask, attributes->repeat, attributes->alpha_map, attributes->alpha_x_origin, attributes->alpha_y_origin, attributes->clip_x_origin, attributes->clip_y_origin, attributes->clip_mask, attributes->graphics_exposures, attributes->subwindow_mode, attributes->poly_edge, attributes->poly_mode, attributes->dither); UNLOCK(); return picture; } /* SetPictureClipRectangles */ void ex_render_set_picture_rectangles (ExDisplay * display, ExRenderPicture * picture, int clip_x_origin, int clip_y_origin, int n_rectangles, ExCoreRectangle * rectangles) { LOCK(); ex_display_send_request (display, "XssL(ssss)", picture, clip_x_origin, clip_y_origin, n_rectangles, rectangles); UNLOCK(); } - notes on format for generating protocl code - "uses keyword" (extensions will generally use the types from core) - some basic types (byte, short, card32, XID, possibly more) - public and private types (public will be typedef'ed and appear in API, private will be exploded) [not sure about this] - type must have representations specified - masked_list type (with defaults, component types must be public) - list_of_blah type (blah must be public) - enumerations (along with representation, if not specified, use smallest possinle) - namespaces (namespace ex { namespace render { ... } }) - errors, events, request - lower case plus underscores, so all coding styles can be generated - need some way to indicate that a given XID is created/destroyed - union For RENDER: namespace ex { namespace render { /* errors */ error pict_format; error picture; error pict_op; error glyph_set; error glyph; /* types */ type picture = XID; type pict_type = enum { indexed, direct }; type pict_op = enum { clear, src, dst, over, over_reverse, in, in_reverse, out, out_reverse, atop, atop_reverse, xor, add, saturate }; type color = struct { card16 red; card16 green; card16 blue; card16 alpha; }; ... private type pict_depth = struct { card8 depth list[pict_visual] visuals }; }} Highlevel notes: - Target users are toolkit authors, Owen, TrollTech, Bill Spitzak, Carl Worth. - smart people - who understand X - and intend to hide ExLib from the users - In other words, the API docs are the protocol specs. The users will need that anyway, and there is no point writing a watered down version of it - Toolkits have their own main loop, ExLib should not try to provide its own - Never send anything on our own, just queue up ontil toolkit flushes - Hmm, trying to send when the buffer size reaches a threshold may not be too bad, if we make sure not to block - potential problem: app's could start using unbounded amounts of memory when they lose connection. GTK+ fix by stopping expose processing exposing when that happens. Perhaps simply a mode "block/non-block" - ExLib must never, ever block, not even for writes The flush method should be bool flush(bool blocking) returning false if blocking is false and the operation would block. Rationale: an application that temprarily loses connection to the X server should not have to block everything else. - Just provide a file descriptor that can be selected on - Provide round-trip free error handling.