Metan's bloghttp://metan.ucw.cz/blog/2019-05-10T00:00:00+02:00About this blog2019-05-10T00:00:00+02:002019-05-10T00:00:00+02:00metantag:metan.ucw.cz,2019-05-10:/blog/about-this-blog.html<p>This place is simply my public logbook for various things.</p>
<p>A few things about myself:</p>
<ul>
<li>I do run on Linux for more than 10 years now</li>
<li>I like to play with electronic and repair vintage equipment</li>
<li>I'm being paid for writing software in C</li>
<li>Sometimes I'm grumpy and I do …</li></ul><p>This place is simply my public logbook for various things.</p>
<p>A few things about myself:</p>
<ul>
<li>I do run on Linux for more than 10 years now</li>
<li>I like to play with electronic and repair vintage equipment</li>
<li>I'm being paid for writing software in C</li>
<li>Sometimes I'm grumpy and I do enjoy it!</li>
</ul>
<p><strong><em>All information on this blog is served without any warranties for correctness or usefulness!</em></strong></p>
<p>email: metan@ucw.cz</p>
<p>irc: metan @ #freenode.net</p>BM520 restoration2019-05-10T00:00:00+02:002019-05-10T00:00:00+02:00metantag:metan.ucw.cz,2019-05-10:/blog/bm520-restoration.html<p>I've bought old, broken and hence cheap, Tesla counter BM520. It's classical
piece of equipment build with TTL 74xx family chips and with nixie tubes for
display.</p>
<p>The good thing about this kind of vintage equipment is that it was made to be
repaired and that there are schematics available …</p><p>I've bought old, broken and hence cheap, Tesla counter BM520. It's classical
piece of equipment build with TTL 74xx family chips and with nixie tubes for
display.</p>
<p>The good thing about this kind of vintage equipment is that it was made to be
repaired and that there are schematics available, you can get even scans of the
original manuals easily from the internet.</p>
<p>This particular unit, apart from being very dusty, had a few broken transistors
and trimming pots in the power unit, which was fairly easy to spot and fix.</p>
<p><a href="http://metan.ucw.cz/blog/images/bm520/bm520.jpg"><img alt="Uncovered BM520" src="http://metan.ucw.cz/blog/images/bm520/bm520-small.jpg"></a></p>
<p>If you are wondering about the specs the maximum frequency is 12.5Mhz although
it seems to be working fine up to 15Mhz for me.</p>
<p>The timebase is driven by a 10Mhz crystal oscillator that is termostated to a
constant temperature and should be correct for six decimal places that are
shown on the display. I haven't verified the precision yet and I would expect
that the frequency is slightly off after all these years.</p>
<p><a href="http://metan.ucw.cz/blog/images/bm520/10Mhz-normal.jpg"><img alt="Termostated 10Mhz normal" src="http://metan.ucw.cz/blog/images/bm520/10Mhz-normal-small.jpg"></a></p>
<p>As I examined the boards I've been surprised that some of the digitron driver
chips 74141 were made by Texas Instruments. Originaly I've thought that this
particular unit has been repaired once already. But later on I've been told
that sometimes Tesla was too slow to produce chips and had to buy them from
capitalistic countries instead.</p>
<p><a href="http://metan.ucw.cz/blog/images/bm520/digitron-drivers.jpg"><img alt="Digitron driver board" src="http://metan.ucw.cz/blog/images/bm520/digitron-drivers-small.jpg"></a></p>
<p>Here we are looking at the power source unit next to the 10Mhz oscillator, the
power transistors are actually mounted on the back of the unit in the classical
TO-3 packages. The board produces two voltage levels, that is +5V for TTL chips
and +12V for amplifiers.</p>
<p>And by the way the connectors on the right are printer interface, the data
shown on the display are available on these two connectors in TTL levels, it
would be quite easy to add USB interface with atmega to capture the measured
data.</p>
<p><a href="http://metan.ucw.cz/blog/images/bm520/psu.jpg"><img alt="PSU" src="http://metan.ucw.cz/blog/images/bm520/psu-small.jpg"></a></p>
<p>There were three faulty transistors in small metal can packages, which is
common mode of failure for these. The two transistors driving the power
transistors were simply dead. While the last transistor disconnected from the
board because one of the leads was completely rusted off. Such transistor can
be easily lifted off the board without much force. There are two reasons why
this happens to old tesla transistors, first of all when there was shortage of
copper transistor leads were made from iron with gold plating on the top. The
iron tends to rust off and eventually you may end up with hollow leads that are
composed mainly of rust and thin gold layer on the top. Secondly the small
metal cans are sealed on the bottom with a glass that contains additives to
lower the melting point, unfortunately some of the additives are also corrosive
which only speeds up process.</p>
<p>As I said earlier three trimming pots needed to be replaced, the wipers were
not making good contact anymore. This usually happens when they are close to
heat source which causes thermal cycling, which may have been the case here,
since the board is stacked between the main transformer and the power
transistors.</p>
<p>Here is my hand-drawn schematic of the PSU with some annotations. The schematic
in the original manual follows how the components are placed on the board,
while this one is divided into a functional blocks.</p>
<p><a href="http://metan.ucw.cz/blog/images/bm520/psu-schematic.png"><img alt="PSU Schematic" src="http://metan.ucw.cz/blog/images/bm520/psu-schematic-small.png"></a></p>
<p>Photo of the swapable boards before cleaning, the two boards on the bottom
separated by a metal shield are wideband amplifiers, the boards on the right
contain the logic circuits implementing the different counter modes in 74xx
silicon.</p>
<p><a href="http://metan.ucw.cz/blog/images/bm520/boards.jpg"><img alt="Swapable boards" src="http://metan.ucw.cz/blog/images/bm520/boards-small.jpg"></a></p>Things I wanted to know about libxcb2019-05-10T00:00:00+02:002019-05-10T00:00:00+02:00metantag:metan.ucw.cz,2019-05-10:/blog/things-i-wanted-to-know-about-libxcb.html<p>I've wanted to look at libxcb for quite some time as the "saner API for X
programming". In the end I managed to write enough code to have a functional X
application, but the main problem is a lack of documentation, which is
unfortunately common for open source project. And …</p><p>I've wanted to look at libxcb for quite some time as the "saner API for X
programming". In the end I managed to write enough code to have a functional X
application, but the main problem is a lack of documentation, which is
unfortunately common for open source project. And since I've written down some
notes it would be shame not to publish them.</p>
<p>Generally libxcb gives you better control over the X connection but you pay by
increased complexity that is needed to implement the same functionality as
compared to classical Xlib. Happily there are <em>xcb-util-*</em> libraries that are
helping a bit.</p>
<h1>Debugging</h1>
<p>You can actually get decent error reporting from libxcb but it's not that
simple. First of all unless you handle errors manually and use the
<em>xcb_foo_checked()</em> function variants, error checking is asynchronous and the
errors are reported as events with <em>response_type</em> zero.</p>
<p>Another complication is that all the error details are encoded as numbers, so
you have to translate these into strings. There is a nice
<a href="https://gitlab.freedesktop.org/xorg/lib/libxcb-errors">xcb-util-error</a> library
that does exactly that, but unfortunatelly it's not present on most distros yet
despite the fact that it has been developed in 2015. Happily it's just a small
library. I've just downloaded the C sources and put them into the project
directory so that it's linked with the program.</p>
<p>Example error handler is here:</p>
<div class="highlight"><pre><span></span><span class="kt">void</span> <span class="nf">handle_event</span><span class="p">(</span><span class="n">xcb_connection_t</span> <span class="o">*</span><span class="n">c</span><span class="p">,</span> <span class="n">xcb_generic_event_t</span> <span class="o">*</span><span class="n">ev</span><span class="p">,</span> <span class="p">...)</span>
<span class="p">{</span>
<span class="k">switch</span> <span class="p">(</span><span class="n">ev</span><span class="o">-></span><span class="n">response_type</span> <span class="o">&</span> <span class="o">~</span><span class="mh">0x80</span><span class="p">)</span> <span class="p">{</span>
<span class="k">case</span> <span class="mi">0</span><span class="o">:</span> <span class="p">{</span>
<span class="n">xcb_generic_error_t</span> <span class="o">*</span><span class="n">err</span> <span class="o">=</span> <span class="p">(</span><span class="n">xcb_generic_error_t</span><span class="o">*</span><span class="p">)</span><span class="n">ev</span><span class="p">;</span>
<span class="n">xcb_errors_context_t</span> <span class="o">*</span><span class="n">err_ctx</span><span class="p">;</span>
<span class="n">xcb_errors_context_new</span><span class="p">(</span><span class="n">c</span><span class="p">,</span> <span class="o">&</span><span class="n">err_ctx</span><span class="p">);</span>
<span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">major</span><span class="p">,</span> <span class="o">*</span><span class="n">minor</span><span class="p">,</span> <span class="o">*</span><span class="n">extension</span><span class="p">,</span> <span class="o">*</span><span class="n">error</span><span class="p">;</span>
<span class="n">major</span> <span class="o">=</span> <span class="n">xcb_errors_get_name_for_major_code</span><span class="p">(</span><span class="n">err_ctx</span><span class="p">,</span> <span class="n">err</span><span class="o">-></span><span class="n">major_code</span><span class="p">);</span>
<span class="n">minor</span> <span class="o">=</span> <span class="n">xcb_errors_get_name_for_minor_code</span><span class="p">(</span><span class="n">err_ctx</span><span class="p">,</span> <span class="n">err</span><span class="o">-></span><span class="n">major_code</span><span class="p">,</span> <span class="n">err</span><span class="o">-></span><span class="n">minor_code</span><span class="p">);</span>
<span class="n">error</span> <span class="o">=</span> <span class="n">xcb_errors_get_name_for_error</span><span class="p">(</span><span class="n">err_ctx</span><span class="p">,</span> <span class="n">err</span><span class="o">-></span><span class="n">error_code</span><span class="p">,</span> <span class="o">&</span><span class="n">extension</span><span class="p">);</span>
<span class="n">printf</span><span class="p">(</span><span class="s">"XCB Error: %s:%s, %s:%s, resource %u sequence %u</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span>
<span class="n">error</span><span class="p">,</span> <span class="n">extension</span> <span class="o">?</span> <span class="nl">extension</span> <span class="p">:</span> <span class="s">"no_extension"</span><span class="p">,</span>
<span class="n">major</span><span class="p">,</span> <span class="n">minor</span> <span class="o">?</span> <span class="nl">minor</span> <span class="p">:</span> <span class="s">"no_minor"</span><span class="p">,</span>
<span class="p">(</span><span class="kt">unsigned</span> <span class="kt">int</span><span class="p">)</span><span class="n">err</span><span class="o">-></span><span class="n">resource_id</span><span class="p">,</span>
<span class="p">(</span><span class="kt">unsigned</span> <span class="kt">int</span><span class="p">)</span><span class="n">err</span><span class="o">-></span><span class="n">sequence</span><span class="p">);</span>
<span class="n">xcb_errors_context_free</span><span class="p">(</span><span class="n">err_ctx</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">case</span> <span class="nl">XCB_BUTTON_PRESS</span><span class="p">:</span>
<span class="p">...</span>
<span class="p">}</span>
<span class="p">...</span>
<span class="p">}</span>
</pre></div>
<p>I've commented out graphic context initialization in my application and got
this error:</p>
<div class="highlight"><pre><span></span>XCB Error: GContext:no_extension, CopyArea:no_minor, resource 31457283 sequence 47
</pre></div>
<p>Here you can easily see that the failure occured at <em>xcb_copy_area()</em>
function and that the problem was wrong gc context parameter, which is much
nicer than staring at black window and trying to figure out what went wrong.</p>
<h1>Blitting a bitmap</h1>
<p>With error reporting in place we can actually start writing actual code that
draws on the screen. In order to do that we have to handle expose events and
repaint particular part of the window. However before we can do that we have to
figure out the format for the pixel data we want to send to the X server and
unfortunatelly the information is scattered around in different structures.</p>
<h2>Querying bitmap format</h2>
<p>We start with a visual, which is a description of a pixel.</p>
<p>When window is created we pass a visual id to the <em>xcb_create_window()</em>
function, which is usually obtained from a <em>xcb_screen_t</em> structure as
<em>screen->root_visual</em>.</p>
<p>We have to match the visual id against list of all supported visuals to get the
<em>xcb_visualtype_t</em> structure with the actual pixel description.</p>
<p>X server supports palette, grayscale and RGB pixels, which are called classes
and are accesible via the <em>visual->_class</em> structure member, see constants
<em>XCB_VISUAL_CLASS_FOO</em>. My application only supports RGB and I doubt that I
will encounter anything else these days, so we skip rest of the formats.</p>
<p>Other mebers describe offsets for the RGB channels in case of RGB pixels, or
number of palette entries for paletted mode, etc.</p>
<div class="highlight"><pre><span></span><span class="n">xcb_visualtype_t</span> <span class="o">*</span><span class="nf">visual_by_id</span><span class="p">(</span><span class="n">xcb_screen_t</span> <span class="o">*</span><span class="n">screen</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">xcb_depth_iterator_t</span> <span class="n">depth_iter</span> <span class="o">=</span> <span class="n">xcb_screen_allowed_depths_iterator</span><span class="p">(</span><span class="n">screen</span><span class="p">);</span>
<span class="kt">int</span> <span class="n">depth</span><span class="p">;</span>
<span class="n">xcb_visuatype_t</span> <span class="o">*</span><span class="n">visual</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>
<span class="k">for</span> <span class="p">(;</span> <span class="n">depth_iter</span><span class="p">.</span><span class="n">rem</span><span class="p">;</span> <span class="n">xcb_depth_next</span><span class="p">(</span><span class="o">&</span><span class="n">depth_iter</span><span class="p">))</span> <span class="p">{</span>
<span class="n">xcb_visualtype_iterator_t</span> <span class="n">visual_iter</span> <span class="o">=</span> <span class="n">xcb_depth_visuals_iterator</span><span class="p">(</span><span class="n">depth_iter</span><span class="p">.</span><span class="n">data</span><span class="p">);</span>
<span class="n">depth</span> <span class="o">=</span> <span class="n">depth_iter</span><span class="p">.</span><span class="n">data</span><span class="o">-></span><span class="n">depth</span><span class="p">;</span>
<span class="k">for</span> <span class="p">(;</span> <span class="n">visual_iter</span><span class="p">.</span><span class="n">rem</span><span class="p">;</span> <span class="n">xcb_visualtype_next</span><span class="p">(</span><span class="o">&</span><span class="n">visual_iter</span><span class="p">))</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">screen</span><span class="o">-></span><span class="n">root_visual</span> <span class="o">==</span> <span class="n">visual_iter</span><span class="p">.</span><span class="n">data</span><span class="o">-></span><span class="n">visual_id</span><span class="p">)</span> <span class="p">{</span>
<span class="n">visual</span> <span class="o">=</span> <span class="n">visual_iter</span><span class="p">.</span><span class="n">data</span><span class="p">;</span>
<span class="k">goto</span> <span class="n">found</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="n">printf</span><span class="p">(</span><span class="s">"Failed to match visual id</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
<span class="k">return</span> <span class="nb">NULL</span><span class="p">;</span>
<span class="nl">found</span><span class="p">:</span>
<span class="k">if</span> <span class="p">(</span><span class="n">visual</span><span class="o">-></span><span class="n">_class</span> <span class="o">!=</span> <span class="n">XCB_VISUAL_CLASS_TRUE_COLOR</span> <span class="o">&&</span>
<span class="n">visual</span><span class="o">-></span><span class="n">_class</span> <span class="o">!=</span> <span class="n">XCB_VISUAL_CLASS_DIRECT_COLOR</span><span class="p">)</span> <span class="p">{</span>
<span class="n">printf</span><span class="p">(</span><span class="s">"Unsupported visual</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
<span class="k">return</span> <span class="nb">NULL</span><span class="p">;</span>
<span class="p">}</span>
<span class="n">printf</span><span class="p">(</span><span class="s">"depth %i, R mask %x G mask %x B mask %x</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span>
<span class="n">depth</span><span class="p">,</span> <span class="n">visual</span><span class="o">-></span><span class="n">red_mask</span><span class="p">,</span> <span class="n">visual</span><span class="o">-></span><span class="n">green_mask</span><span class="p">,</span> <span class="n">visual</span><span class="o">-></span><span class="n">blue_mask</span><span class="p">);</span>
<span class="k">return</span> <span class="n">visual</span><span class="p">;</span>
<span class="p">}</span>
</pre></div>
<p>The visual itself is not enough in order to create the pixel buffer, we also
need a format, that describes how long is our pixel, it's common that pixels
are encoded in RGB 8 bits per channel while pixel is 32 bits long with 8 bits
of unused space. For that we actually have to locate pixel format.</p>
<div class="highlight"><pre><span></span><span class="kt">int</span> <span class="nf">bpp_by_depth</span><span class="p">(</span><span class="n">xcb_connection_t</span> <span class="o">*</span><span class="n">c</span><span class="p">,</span> <span class="kt">int</span> <span class="n">depth</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">const</span> <span class="n">xcb_setup_t</span> <span class="o">*</span><span class="n">setup</span> <span class="o">=</span> <span class="n">xcb_get_setup</span><span class="p">(</span><span class="n">c</span><span class="p">);</span>
<span class="n">xcb_format_iterator_t</span> <span class="n">fmt_iter</span> <span class="o">=</span> <span class="n">xcb_setup_pixmap_formats_iterator</span><span class="p">(</span><span class="n">setup</span><span class="p">);</span>
<span class="k">for</span> <span class="p">(;</span> <span class="n">fmt_iter</span><span class="p">.</span><span class="n">rem</span><span class="p">;</span> <span class="n">xcb_format_next</span><span class="p">(</span><span class="o">&</span><span class="n">fmt_iter</span><span class="p">))</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">fmt_iter</span><span class="p">.</span><span class="n">data</span><span class="o">-></span><span class="n">depth</span> <span class="o">==</span> <span class="n">depth</span><span class="p">)</span>
<span class="k">return</span> <span class="n">fmt_iter</span><span class="p">.</span><span class="n">data</span><span class="o">-></span><span class="n">bits_per_pixel</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</pre></div>
<p>With that we can calculate the size of image buffer as
<em>bpp_by_depth(c, depth) * w * h</em> and we know that channels are organized as
described in the visual type structure.</p>
<p>Note also that the format data structure has also a <code>scanline_pad</code> member,
it's common for the image horizontal lines to be aligned to 32bit boundaries a
for performance reasons. Here we omit the padd as in the most of the cases we
get 32bit depth on modern hardware which is aligned by definition.</p>
<h2>Preparing a backing pixmap</h2>
<p>Now that we know the exact pixmap format we can start with the drawing code.
There are actually two options how to send pixmap to the X server. Either we
send it with <em>xcb_put_image()</em> or, if the server and application are running on
the same physical machine, we can use SHM which about one order of magnitude
faster than passing the data via socket. In both cases backing pixmap will be
created, which is then copied over the window on the expose event with
<em>xcb_copy_area()</em>.</p>
<p>The basic code that does not use SHM looks like this, the window and
<em>pixel_buffer</em> initialization is left as an excercise to the reader:</p>
<div class="highlight"><pre><span></span><span class="n">xcb_screen_t</span> <span class="o">*</span><span class="n">screen</span><span class="p">;</span>
<span class="n">xcb_window_t</span> <span class="n">win</span><span class="p">;</span>
<span class="n">xcb_gcontext_t</span> <span class="n">gc</span><span class="p">;</span>
<span class="n">xcb_pixmap_t</span> <span class="n">backing_pixmap</span><span class="p">;</span>
<span class="k">struct</span> <span class="n">pixel_buffer</span> <span class="p">{</span>
<span class="kt">int</span> <span class="n">w</span><span class="p">;</span>
<span class="kt">int</span> <span class="n">h</span><span class="p">;</span>
<span class="kt">int</span> <span class="n">bytes_per_row</span><span class="p">;</span>
<span class="kt">void</span> <span class="o">*</span><span class="n">pixels</span><span class="p">;</span>
<span class="p">}</span> <span class="n">buffer</span><span class="p">;</span>
<span class="kt">void</span> <span class="nf">init_gc</span><span class="p">(</span><span class="n">xcb_connection_t</span> <span class="o">*</span><span class="n">c</span><span class="p">)</span>
<span class="p">{</span>
<span class="kt">uint32_t</span> <span class="n">values</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span><span class="n">screen</span><span class="o">-></span><span class="n">black_pixel</span><span class="p">,</span> <span class="n">screen</span><span class="o">-></span><span class="n">white_pixel</span><span class="p">};</span>
<span class="n">gc</span> <span class="o">=</span> <span class="n">xcb_generate_id</span><span class="p">(</span><span class="n">c</span><span class="p">);</span>
<span class="n">xcb_create_gc</span><span class="p">(</span><span class="n">c</span><span class="p">,</span> <span class="n">gc</span><span class="p">,</span> <span class="n">backing_pixmap</span><span class="p">,</span> <span class="n">XCB_GC_FOREGROUND</span> <span class="o">|</span> <span class="n">XCB_GC_BACKGROUND</span><span class="p">,</span> <span class="n">values</span><span class="p">);</span>
<span class="p">}</span>
<span class="kt">void</span> <span class="nf">init_backing_pixmap</span><span class="p">(</span><span class="n">xcb_connection_t</span> <span class="o">*</span><span class="n">c</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">pixmap</span> <span class="o">=</span> <span class="n">xcb_generate_id</span><span class="p">(</span><span class="n">c</span><span class="p">);</span>
<span class="n">xcb_create_pixmap</span><span class="p">(</span><span class="n">c</span><span class="p">,</span> <span class="n">screen</span><span class="o">-></span><span class="n">root_depth</span><span class="p">,</span> <span class="n">backing_pixmap</span><span class="p">,</span> <span class="n">win</span><span class="p">,</span> <span class="n">w</span><span class="p">,</span> <span class="n">h</span><span class="p">);</span>
<span class="n">init_gc</span><span class="p">(</span><span class="n">c</span><span class="p">);</span>
<span class="p">}</span>
<span class="cm">/*</span>
<span class="cm"> * Note that without copying the data to a temporary buffer we can send updates</span>
<span class="cm"> * only as a horizontal stripes.</span>
<span class="cm"> */</span>
<span class="kt">void</span> <span class="nf">update_backing_pixmap</span><span class="p">(</span><span class="n">xcb_connection_t</span> <span class="o">*</span><span class="n">c</span><span class="p">,</span> <span class="kt">int</span> <span class="n">x</span><span class="p">,</span> <span class="kt">int</span> <span class="n">y</span><span class="p">,</span> <span class="kt">int</span> <span class="n">w</span><span class="p">,</span> <span class="kt">int</span> <span class="n">h</span><span class="p">)</span>
<span class="p">{</span>
<span class="cm">/* Send image data to X server */</span>
<span class="n">xcb_put_image</span><span class="p">(</span><span class="n">c</span><span class="p">,</span> <span class="n">XCB_IMAGE_FORMAT_Z_PIXMAP</span><span class="p">,</span> <span class="n">backing_pixmap</span><span class="p">,</span>
<span class="n">gc</span><span class="p">,</span> <span class="n">w</span><span class="p">,</span> <span class="n">h</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span>
<span class="n">screen</span><span class="o">-></span><span class="n">root_depth</span><span class="p">,</span> <span class="n">buffer</span><span class="p">.</span><span class="n">bytes_per_row</span> <span class="o">*</span> <span class="n">h</span><span class="p">,</span>
<span class="n">buffer</span><span class="p">.</span><span class="n">pixels</span> <span class="o">+</span>
<span class="n">buffer</span><span class="p">.</span><span class="n">bytes_per_row</span> <span class="o">*</span> <span class="n">y</span><span class="p">);</span>
<span class="cm">/* Copy updated data to window */</span>
<span class="n">xcb_copy_area</span><span class="p">(</span><span class="n">c</span><span class="p">,</span> <span class="n">backing_pixmap</span><span class="p">,</span> <span class="n">win</span><span class="p">,</span> <span class="n">gc</span><span class="p">,</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> <span class="n">w</span><span class="p">,</span> <span class="n">h</span><span class="p">);</span>
<span class="n">xcb_flush</span><span class="p">(</span><span class="n">c</span><span class="p">);</span>
<span class="p">}</span>
</pre></div>
<p>And finally the event handling that is the same both for the SHM and non-SHM case:</p>
<div class="highlight"><pre><span></span><span class="kt">void</span> <span class="nf">handle_event</span><span class="p">(</span><span class="n">xcb_connection_t</span> <span class="o">*</span><span class="n">c</span><span class="p">,</span> <span class="n">xcb_generic_event_t</span> <span class="o">*</span><span class="n">ev</span><span class="p">,</span> <span class="p">...)</span>
<span class="p">{</span>
<span class="k">switch</span> <span class="p">(</span><span class="n">ev</span><span class="o">-></span><span class="n">response_type</span> <span class="o">&</span> <span class="o">~</span><span class="mh">0x80</span><span class="p">)</span> <span class="p">{</span>
<span class="p">...</span>
<span class="k">case</span> <span class="nl">XCB_EXPOSE</span><span class="p">:</span> <span class="p">{</span>
<span class="n">xcb_expose_event_t</span> <span class="o">*</span><span class="n">eev</span> <span class="o">=</span> <span class="p">(</span><span class="n">xcb_expose_event_t</span> <span class="o">*</span><span class="p">)</span><span class="n">ev</span><span class="p">;</span>
<span class="n">xcb_copy_area</span><span class="p">(</span><span class="n">c</span><span class="p">,</span> <span class="n">backing_pixmap</span><span class="p">,</span> <span class="n">win</span><span class="p">,</span> <span class="n">gc</span><span class="p">,</span>
<span class="n">eev</span><span class="o">-></span><span class="n">x</span><span class="p">,</span> <span class="n">eev</span><span class="o">-></span><span class="n">y</span><span class="p">,</span>
<span class="n">eev</span><span class="o">-></span><span class="n">x</span><span class="p">,</span> <span class="n">eev</span><span class="o">-></span><span class="n">y</span><span class="p">,</span>
<span class="n">eev</span><span class="o">-></span><span class="n">width</span><span class="p">,</span> <span class="n">eev</span><span class="o">-></span><span class="n">height</span><span class="p">);</span>
<span class="n">xcb_flush</span><span class="p">(</span><span class="n">c</span><span class="p">);</span>
<span class="p">}</span> <span class="k">break</span><span class="p">;</span>
<span class="p">}</span>
</pre></div>
<h2>Is that all?</h2>
<p>No, not really, there are a couple of things that still needs to be taken care
of.</p>
<p>First of all there is a maximum request size that X server can handle, my X
server returns maximal request size about 16MB, which is mostly fine unless I
had display resolution over about 2000x2000 pixels, in that case full screen
window backing pixmap would be over the size.</p>
<p>Hence we have to make sure the request for <em>xcb_put_image()</em> is smaller than
that and split the request into several vertical stripes if it's not. The code
for that would look like:</p>
<div class="highlight"><pre><span></span><span class="kt">void</span> <span class="nf">update_backing_pixmap</span><span class="p">(</span><span class="n">xcb_connection_t</span> <span class="o">*</span><span class="n">c</span><span class="p">,</span> <span class="kt">int</span> <span class="n">x</span><span class="p">,</span> <span class="kt">int</span> <span class="n">y</span><span class="p">,</span> <span class="kt">int</span> <span class="n">w</span><span class="p">,</span> <span class="kt">int</span> <span class="n">h</span><span class="p">)</span>
<span class="p">{</span>
<span class="kt">size_t</span> <span class="n">req_len</span> <span class="o">=</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">xcb_image_request_t</span><span class="p">);</span>
<span class="kt">size_t</span> <span class="n">len</span> <span class="o">=</span> <span class="p">(</span><span class="n">req_len</span> <span class="o">+</span> <span class="n">buffer</span><span class="p">.</span><span class="n">bytes_per_row</span> <span class="o">*</span> <span class="n">h</span><span class="p">)</span> <span class="o">>></span> <span class="mi">2</span><span class="p">;</span>
<span class="kt">uint64_t</span> <span class="n">max_req_len</span> <span class="o">=</span> <span class="n">xcb_get_maximum_request_length</span><span class="p">(</span><span class="n">c</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="n">len</span> <span class="o"><</span> <span class="n">max_req_len</span><span class="p">)</span> <span class="p">{</span>
<span class="n">xcb_put_image</span><span class="p">(...);</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="kt">int</span> <span class="n">rows_per_request</span> <span class="o">=</span> <span class="p">(</span><span class="n">max_req_len</span><span class="o"><<</span><span class="mi">2</span> <span class="o">-</span> <span class="n">req_len</span><span class="p">)</span> <span class="o">/</span> <span class="n">buffer</span><span class="p">.</span><span class="n">bytes_per_row</span><span class="p">;</span>
<span class="kt">int</span> <span class="n">y_off</span><span class="p">;</span>
<span class="k">for</span> <span class="p">(</span><span class="n">y_off</span> <span class="o">=</span> <span class="n">y</span><span class="p">;</span> <span class="n">y_off</span> <span class="o">+</span> <span class="n">rows_per_request</span> <span class="o"><</span> <span class="n">h</span><span class="p">;</span> <span class="n">y_off</span> <span class="o">+=</span> <span class="n">rows_per_request</span><span class="p">)</span> <span class="p">{</span>
<span class="n">xcb_put_image</span><span class="p">(</span><span class="n">c</span><span class="p">,</span> <span class="n">XCB_IMAGE_FORMAT_Z_PIXMAP</span><span class="p">,</span> <span class="n">backing_pixmap</span><span class="p">,</span> <span class="n">gc</span><span class="p">,</span>
<span class="n">buffer</span><span class="p">.</span><span class="n">w</span><span class="p">,</span> <span class="n">rows</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span>
<span class="n">screen</span><span class="o">-></span><span class="n">root_depth</span><span class="p">,</span>
<span class="n">rows</span> <span class="o">*</span> <span class="n">buffer</span><span class="p">.</span><span class="n">bytes_per_row</span><span class="p">,</span>
<span class="n">buffer</span><span class="p">.</span><span class="n">pixels</span> <span class="o">+</span> <span class="n">y</span> <span class="o">*</span> <span class="n">buffer</span><span class="p">.</span><span class="n">bytes_per_row</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">...</span>
<span class="p">}</span>
</pre></div>
<p>And it's a good idea to send the image updates in a strips anyways, which is by
the way, how the original Xlib does it, because that way the user will see the
window being repainted slowly rather than waiting for the one big update once
the whole image was transmitted over network.</p>
<h2>SHM Images</h2>
<p>The X server extension is build on the top of the SysV SHM. The application
creates a SHM region, attaches it to the server.</p>
<div class="highlight"><pre><span></span>int attach_shm(xcb_connection_t *c, int w, int h, int depth, void **addr, xcb_shm_seg_t *shm_seg)
{
size_t size = w * h * bpp_by_depth(c, depth);
int shm_id;
shm_id = shmget(IPC_PRIVATE, size, IPC_CREAT | 0600);
if (shm_id == -1) {
perror("shmget()");
return 0;
}
*addr = shmat(shm_id, 0, 0);
if (*addr == (void*)-1) {
perror("shmat()");
shmctl(shm_id, IPC_RMID, 0);
return 0;
}
*shm_seg = xcb_generate_id();
xcb_void_cookie_t cookie;
xcb_generic_error_t *err;
cookie = xcb_shm_attach_checked(c, *shm_seg, shm_id, 0);
err = xcb_request_check(c, cookie);
shmctl(shm_id, IPC_RMID, 0);
if (err) {
fprintf(stderr, "xcb_shm_attach_checked() failed");
free(err);
return 0;
}
return 1;
}
</pre></div>
<p>There are actually two different SHM modes and that depends on where the X
server keeps the image data.</p>
<p>If the server keeps the data in RAM the SHM query reply info has the
<em>shared_pixmaps</em> flag turned on you can create a pixmap from the SHM segment
with <em>xcb_shm_create_pixmap()</em> which then behaves as any other drawable. That
means that we can pass it to functions such as <em>xcb_copy_area()</em>.</p>
<p>However if the X server keeps the images in graphic card memory we cannot
simply <em>memcpy()</em> a data between these buffers and coputer RAM and we have to
use the <em>xcb_shm_put_image()</em> instead. If you are wondering yes this mode is
still fast, it's just ugly implementation details that are leaking into the
application API.</p>
<div class="highlight"><pre><span></span>int query_shm(xcb_connection_t *c)
{
xcb_shm_query_version_reply_t *rep;
xcb_shm_query_version_cookie_t ck;
ck = xcb_shm_query_version(c);
rep = xcb_shm_query_version_reply(c, ck, NULL);
if (!rep) {
fprintf(stderr, "Failed to get SHM reply\n");
return 0;
}
fprintf(stderr, "SHM verision %i.%i\n", rep->major_version, rep->minor_version);
if (rep->shared_pixmaps)
fprintf(stderr, "Pixmaps are shared\n");
if (rep->major_version < 1 ||
rep->major_version == 1 && rep->minor_version == 0)
return 0;
return 1;
}
</pre></div>
<p>And lastly but not least as the SHM operations are asynchronous we need
notifications when the server has finished copying the data from the buffer so
that we can refrain from modifying the buffer during that period. For that to
happen we can ask the functions to send a completion event that will, later on,
land in our event queue. Unfortunatelly the event ids for extensions, remember
the SHM is extension, are dynamically allocated so the event type is computed
from SHM extension first event plus the <em>XCB_SHM_COMPLETION</em> offset.</p>
<div class="highlight"><pre><span></span>int shm_completion_type_id(xcb_connection_t *c)
{
return xcb_get_extension_data(c, &xcb_shm_id)->first_event + XCB_SHM_COMPLETION;
}
</pre></div>
<h2>SHM sad story</h2>
<p>There is unfortunately no way how to figure out if it's safe to use SHM or not.</p>
<p>Most applications just attempt to do the SHM intialization and fall back to
sending the image over a socket if that fails. Which mostly works, but it may
also happen that X sever will pick up unrelated SHM memory with the same id on
a remote machine. I soved that by a simple hack that checks the '$DISPLAY'
variable and turns off SHM if it does not start with colon, which seems to work
most of the time.</p>