X11 Getting Title Bar Size - c

I'm writing a simple text user interface with libX11 and I've got a set of code working for a basic library abstraction.
The problem is when I try to draw text, the title bar blocks the first line. So now I have to offset the vertical printing by about 20 pixels. Is there a way to determine the exact size of the title bar, so that I can properly offset each line of text?
Here's the code I have so far, and tui_puts is the function I need to do this for:
#include "tui.h"
#include <X11/Xlib.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
struct tui_data {
Display * display;
Window id_window;
GC context_graphics;
XIM method_input;
XIC context_input;
XWindowAttributes spec_window;
XFontStruct * spec_font;
size_t offset_y;
size_t offset_x;
};
int tui_init (struct tui_session * ptr_tui){
memset (ptr_tui, 0, sizeof (struct tui_session));
ptr_tui->ptr_data = calloc (sizeof (struct tui_data), 1);
if (!ptr_tui->ptr_data) return -1;
ptr_tui->ptr_data->display = XOpenDisplay (0);
if (!ptr_tui->ptr_data->display) return -1;
int color_black = BlackPixel(ptr_tui->ptr_data->display, DefaultScreen(ptr_tui->ptr_data->display));
int color_white = WhitePixel(ptr_tui->ptr_data->display, DefaultScreen(ptr_tui->ptr_data->display));
ptr_tui->ptr_data->id_window = XCreateSimpleWindow(ptr_tui->ptr_data->display,
DefaultRootWindow(ptr_tui->ptr_data->display),
0, 0, 640, 480, 0, color_black, color_black);
XSelectInput (ptr_tui->ptr_data->display,
ptr_tui->ptr_data->id_window,
StructureNotifyMask);
XMapWindow (ptr_tui->ptr_data->display,
ptr_tui->ptr_data->id_window);
ptr_tui->ptr_data->context_graphics = XCreateGC(ptr_tui->ptr_data->display,
ptr_tui->ptr_data->id_window, 0, 0);
for (;;){
XEvent e;
XNextEvent (ptr_tui->ptr_data->display, &e);
if (e.type == MapNotify) break;
}
XSetForeground (ptr_tui->ptr_data->display,
ptr_tui->ptr_data->context_graphics,
color_white);
XGetWindowAttributes (ptr_tui->ptr_data->display,
ptr_tui->ptr_data->id_window,
&ptr_tui->ptr_data->spec_window);
XSelectInput (ptr_tui->ptr_data->display,
ptr_tui->ptr_data->id_window,
ExposureMask | KeyPressMask | ResizeRedirectMask);
ptr_tui->ptr_data->method_input = XOpenIM (ptr_tui->ptr_data->display, 0, 0, 0);
if (!ptr_tui->ptr_data->method_input){
return -1;
}
ptr_tui->ptr_data->context_input = XCreateIC (ptr_tui->ptr_data->method_input,
XNInputStyle, XIMPreeditNothing
| XIMStatusNothing, XNClientWindow,
ptr_tui->ptr_data->id_window, NULL);
if (!ptr_tui->ptr_data->context_input){
return -1;
}
XSetICFocus (ptr_tui->ptr_data->context_input);
ptr_tui->ptr_data->spec_font = XLoadQueryFont (ptr_tui->ptr_data->display,
"-misc-fixed-medium-"
"r-normal-*-*-120-*-*");
if (!ptr_tui->ptr_data->spec_font){
return -1;
}
XSetFont (ptr_tui->ptr_data->display,
ptr_tui->ptr_data->context_graphics,
ptr_tui->ptr_data->spec_font->fid);
ptr_tui->max_x = ptr_tui->ptr_data->spec_window.width
/ ptr_tui->ptr_data->spec_font->max_bounds.width;
ptr_tui->max_y = ptr_tui->ptr_data->spec_window.height
/ (ptr_tui->ptr_data->spec_font->max_bounds.ascent
+ ptr_tui->ptr_data->spec_font->max_bounds.descent);
return 0;
}
int tui_free (struct tui_session * ptr_tui){
if (ptr_tui->ptr_data){
XFreeFont (ptr_tui->ptr_data->display, ptr_tui->ptr_data->spec_font);
XFreeGC (ptr_tui->ptr_data->display, ptr_tui->ptr_data->context_graphics);
XSetCloseDownMode(ptr_tui->ptr_data->display, DestroyAll);
XCloseDisplay (ptr_tui->ptr_data->display);
free (ptr_tui->ptr_data);
}
memset (ptr_tui, 0, sizeof (struct tui_session));
return 0;
}
int tui_refresh (struct tui_session * ptr_tui){
XFlush (ptr_tui->ptr_data->display);
return 0;
}
int tui_clear (struct tui_session * ptr_tui){
XClearWindow (ptr_tui->ptr_data->display,
ptr_tui->ptr_data->id_window);
return 0;
}
int tui_puts (struct tui_session * ptr_tui,
const void * ptr_data, size_t len_data,
size_t index_x, size_t index_y, uint32_t flags){
if (((index_x + len_data) >= ptr_tui->max_x)
|| (index_y >= ptr_tui->max_y)) return -1;
XDrawImageString (ptr_tui->ptr_data->display,
ptr_tui->ptr_data->id_window,
ptr_tui->ptr_data->context_graphics,
ptr_tui->ptr_data->spec_window.border_width
+ ptr_tui->ptr_data->spec_font->max_bounds.width * index_x,
(ptr_tui->ptr_data->spec_font->max_bounds.ascent
+ ptr_tui->ptr_data->spec_font->max_bounds.descent)
* index_y + 20, ptr_data, len_data);
return 0;
}
int tui_message (struct tui_session * ptr_tui, const char * format, ...){
return -1;
}
int tui_prompt (struct tui_session * ptr_tui, const char * format, ...){
return -1;
}
int tui_input (struct tui_session * ptr_tui, tui_event * ptr_event){
XEvent event;
Status status = 0;
KeySym keysym = 0;
char buffer[4];
int count = 0;
XNextEvent(ptr_tui->ptr_data->display, &event);
switch (event.type){
case KeyPress:
count = Xutf8LookupString (ptr_tui->ptr_data->context_input,
(XKeyPressedEvent*) &event, buffer,
sizeof (buffer), &keysym, &status);
if (count){
ptr_event->type = TUI_EVENT_KEYPRESS;
ptr_event->value = buffer[0];
}
break;
case Expose:
ptr_event->type = TUI_EVENT_REPRINT;
ptr_event->value = 0;
break;
case ResizeRequest:
XGetWindowAttributes (ptr_tui->ptr_data->display,
ptr_tui->ptr_data->id_window,
&ptr_tui->ptr_data->spec_window);
ptr_event->type = TUI_EVENT_REPRINT;
ptr_event->value = 0;
break;
default:
ptr_event->type = 0;
ptr_event->value = 0;
}
return 0;
}
int tui_scroll (struct tui_session * ptr_tui, int offset){
return -1;
}
Also, here's an important structure:
typedef struct {
int x, y; /* location of window */
int width, height; /* width and height of window */
int border_width; /* border width of window */
int depth; /* depth of window */
Visual *visual; /* the associated visual structure */
Window root; /* root of screen containing window */
int class; /* InputOutput, InputOnly*/
int bit_gravity; /* one of the bit gravity values */
int win_gravity; /* one of the window gravity values */
int backing_store; /* NotUseful, WhenMapped, Always */
unsigned long backing_planes; /* planes to be preserved if possible */
unsigned long backing_pixel; /* value to be used when restoring planes */
Bool save_under; /* boolean, should bits under be saved? */
Colormap colormap; /* color map to be associated with window */
Bool map_installed; /* boolean, is color map currently installed*/
int map_state; /* IsUnmapped, IsUnviewable, IsViewable */
long all_event_masks; /* set of events all people have interest in*/
long your_event_mask; /* my event mask */
long do_not_propagate_mask; /* set of events that should not propagate */
Bool override_redirect; /* boolean value for override-redirect */
Screen *screen; /* back pointer to correct screen */
} XWindowAttributes;
And this was helpful for getting the horizontal offset of the client area, but I don't see anything that would help me get the vertical offset.

The origin is at the base line, so you have to add ptr_tui->ptr_data->spec_font->max_bounds.ascent instead of 20 if I'm not lost in your code.

Related

Trying to play a WAV with SDL but getting buzzing noise

I'm writing this simple program to learn how to work with audio with SDL. The program opens a window with a button that says go and when you hit the button it should play a WAV of a drum sample. There are no errors and the program doesn't crash it's just that instead of playing the drum sound it makes a weird buzzing noise that doesn't stop until you exit the program. I think it's something to do with the audio callback function but I am very new to this so I'm not sure exactly how it works.
#include <SDL2/SDL.h>
#include <SDL2/SDL_ttf.h>
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include <stdlib.h>
#include <SDL2/SDL_audio.h>
TTF_Font* font;
SDL_Color white = {255, 255, 255};
SDL_AudioSpec frmt;
bool run = true;
bool playing = false;
struct sample
{
Uint8 *data;
Uint32 dpos;
Uint32 dlen;
} sound;
bool buttonPress(int x, int y, SDL_Rect r)
{
if (x < (r.x + r.w) && x > r.x)
{
if (y < (r.y + r.h) && y > r.y)
{
return true;
}
}
return false;
}
void mixing(void *unused, Uint8 *stream, int len)
{
Uint32 amount;
if (playing)
{
amount = sound.dlen - sound.dpos;
if ( amount > len ) {
amount = len;
}
SDL_MixAudio(stream, &sound.data[sound.dpos], amount, SDL_MIX_MAXVOLUME);
sound.dpos += amount;
}
}
int main(int argc, char *argv[])
{
SDL_Init(SDL_INIT_EVERYTHING);
TTF_Init();
frmt.channels = 1;
frmt.format = AUDIO_S16;
frmt.freq = 22050;
frmt.samples = 512;
frmt.userdata = NULL;
frmt.callback = mixing;
int mX, mY;
Uint32 fpsTimer = SDL_GetTicks();
font = TTF_OpenFont("octaFont.TTF", 20);
SDL_Window* window = SDL_CreateWindow("Test", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 400, 200, 0);
SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
SDL_Surface* surface = TTF_RenderText_Solid(font, "Go!", white);
SDL_Texture* texture = SDL_CreateTextureFromSurface(renderer, surface);
SDL_Rect button;
button.w = 50;
button.h = 35;
button.x = 10;
button.y = 10;
SDL_Rect bg;
bg.w = 400;
bg.h = 200;
bg.x = 0;
bg.y = 0;
SDL_Event e;
if (SDL_OpenAudio(&frmt, NULL) < 0)
{
fprintf(stderr, "Unable to open audio: %s\n", SDL_GetError());
return 1;
}
SDL_PauseAudio(0);
SDL_AudioSpec wave;
SDL_AudioCVT cvt;
Uint8 *data;
Uint32 dlen;
if (SDL_LoadWAV("sound.wav", &wave, &data, &dlen) == NULL)
{
fprintf(stderr, "Unable to load wave: %s\n", SDL_GetError());
return 1;
}
SDL_BuildAudioCVT(&cvt, wave.format, wave.channels, wave.freq, AUDIO_S16, 1, 22050);
cvt.buf = malloc(dlen*cvt.len_mult);
memcpy(cvt.buf, data, dlen);
cvt.len = dlen;
SDL_ConvertAudio(&cvt);
SDL_FreeWAV(data);
SDL_LockAudio();
free(sound.data);
sound.data = cvt.buf;
sound.dlen = cvt.len_cvt;
sound.dpos = 0;
SDL_UnlockAudio();
while (run)
{
while (SDL_PollEvent(&e))
{
switch (e.type)
{
case SDL_QUIT:
run = false;
break;
case SDL_KEYDOWN:
switch (e.key.keysym.sym)
{
case SDLK_SPACE:
playing = false;
break;
default:
break;
}
break;
case SDL_MOUSEBUTTONDOWN:
SDL_GetMouseState(&mX, &mY);
if (buttonPress(mX, mY, button))
{
playing = true;
}
break;
default:
break;
}
}
if (SDL_GetTicks() - fpsTimer >= 16)
{
SDL_RenderClear(renderer);
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
SDL_RenderFillRect(renderer, &bg);
SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
SDL_RenderDrawRect(renderer, &button);
SDL_RenderCopy(renderer, texture, NULL, &button);
SDL_RenderPresent(renderer);
fpsTimer = SDL_GetTicks();
}
}
SDL_CloseAudio();
SDL_DestroyWindow(window);
SDL_DestroyRenderer(renderer);
TTF_Quit();
SDL_Quit();
return 0;
}
The issue may be that you're using SDL_MixAudio.
Your code mixes in the data from the .wav file, but mixes it into what?
What is in stream before calling SDL_MixAudio?
I get the same buzz, but when I use memset to zero out the stream buffer before calling SDL_MixAudio, I get the correct sound from the .wav file.
I don't know for sure but I believe that on the second and subsequent calls to your mixing function, the data from the prior callback is still in the stream buffer.
Side note: When the .wav data has finished playing, we can reset sound.dpos to replay the file again.
Here's the refactored code:
void
mixing(void *unused, Uint8 *stream, int len)
{
Uint32 amount;
// NOTE/DEBUG: force sound on
#if 1
playing = 1;
#endif
// NOTE/FIX: zero out stream data so we don't mix in random data (from the
// prior round)
#if 1
memset(stream,0,len);
#endif
if (playing) {
amount = sound.dlen - sound.dpos;
if (amount > len) {
amount = len;
}
#if 1
SDL_MixAudio(stream, &sound.data[sound.dpos], amount, SDL_MIX_MAXVOLUME);
#else
memcpy(stream,&sound.data[sound.dpos],amount);
#endif
sound.dpos += amount;
// NOTE/FIX(?) -- start wav at beginning after all data has been output
#if 1
if (sound.dpos >= sound.dlen)
sound.dpos = 0;
#endif
}
}

Create a System Tray Icon in Linux without GTK/QT

I need to create a Tray Icon for my application using purely the Xlib. After some searching, reading the XEmbed documentation and some stuff on SO, this is what the code looks like so far (mostly copy & paste). I understand what it does, but not why. It is creating a tray icon appearantly, but only 1px wide in my mate-panels area. What am I doing wrong? Why is it not the size of a normal tray icon?
Here is my code:
#include <X11/Xutil.h>
#include <string.h>
#define MIN(A, B) ((A) < (B) ? (A) : (B))
/* --------- XEMBED and systray stuff */
#define SYSTEM_TRAY_REQUEST_DOCK 0
#define SYSTEM_TRAY_BEGIN_MESSAGE 1
#define SYSTEM_TRAY_CANCEL_MESSAGE 2
static int trapped_error_code = 0;
static int (*old_error_handler) (Display *, XErrorEvent *);
static int
error_handler(Display *display, XErrorEvent *error) {
trapped_error_code = error->error_code;
return 0;
}
void
trap_errors(void) {
trapped_error_code = 0;
old_error_handler = XSetErrorHandler(error_handler);
}
int
untrap_errors(void) {
XSetErrorHandler(old_error_handler);
return trapped_error_code;
}
void
send_systray_message(Display* dpy, long message, long data1, long data2, long data3) {
XEvent ev;
Atom selection_atom = XInternAtom (dpy,"_NET_SYSTEM_TRAY_S0",False);
Window tray = XGetSelectionOwner (dpy,selection_atom);
if ( tray != None)
XSelectInput (dpy,tray,StructureNotifyMask);
memset(&ev, 0, sizeof(ev));
ev.xclient.type = ClientMessage;
ev.xclient.window = tray;
ev.xclient.message_type = XInternAtom (dpy, "_NET_SYSTEM_TRAY_OPCODE", False );
ev.xclient.format = 32;
ev.xclient.data.l[0] = CurrentTime;
ev.xclient.data.l[1] = message;
ev.xclient.data.l[2] = data1; // <--- your window is only here
ev.xclient.data.l[3] = data2;
ev.xclient.data.l[4] = data3;
trap_errors();
XSendEvent(dpy, tray, False, NoEventMask, &ev);
XSync(dpy, False);
usleep(10000);
if (untrap_errors()) {
/* Handle errors */
}
}
/* ------------ Regular X stuff */
int
main(int argc, char **argv) {
int width, height;
XWindowAttributes wa;
XEvent ev;
Display *dpy;
int screen;
Window root, win;
/* init */
if (!(dpy=XOpenDisplay(NULL)))
return 1;
screen = DefaultScreen(dpy);
root = RootWindow(dpy, screen);
if(!XGetWindowAttributes(dpy, root, &wa))
return 1;
width = height = MIN(wa.width, wa.height);
/* create window */
win = XCreateSimpleWindow(dpy, root, 0, 0, width, height, 0, 0, 0x7F7F997F);
send_systray_message(dpy, SYSTEM_TRAY_REQUEST_DOCK, win, 0, 0); // pass win only once
XMapWindow(dpy, win);
XSync(dpy, False);
/* run */
while(1) {
while(XPending(dpy)) {
XNextEvent(dpy, &ev); /* just waiting until we error because window closed */
}
}
}
I am thankful for everything that will point me in the right direction, even if its just some documentation that I have not found so far.

Handle C-q with XLookupString

This is a working example of X11 code that handles Ctrl-q event to quit application:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xos.h>
void exitOnCondition(char cond, const char *msg, int exitCode, Display *dpy, Window *w, GC *gc) {
if(cond) {
printf("%s\n", msg);
if(dpy && gc) XFreeGC(dpy, *gc);
if(dpy && w) XDestroyWindow(dpy, *w);
if(dpy) XCloseDisplay(dpy);
exit(exitCode);
}
}
int main(int argc, char **argv) {
Display *dpy = XOpenDisplay(0);
exitOnCondition(dpy == 0, "Error: XOpenDisplay failed", -1, dpy, 0, 0);
int blackColor = BlackPixel(dpy, DefaultScreen(dpy));
int whiteColor = WhitePixel(dpy, DefaultScreen(dpy));
Window w = XCreateSimpleWindow(dpy, DefaultRootWindow(dpy), 0, 0,
200, 100, 0, blackColor, blackColor);
//Tell X Server to send MapNotify events
XSelectInput(dpy, w, StructureNotifyMask | KeyPressMask);
//Make window appear
XMapWindow(dpy, w);
//Graphics Context
GC gc = XCreateGC(dpy, w, 0, 0);
//Set white color for drawing
XSetForeground(dpy, gc, whiteColor);
//Wait for the MapNotify event
for(;;) {
XEvent e;
XNextEvent(dpy, &e);
if (e.type == MapNotify) {
break;
}
}
//Draw the line
XDrawLine(dpy, w, gc, 10, 60, 180, 20);
//Send the "DrawLine" request to the server
XFlush(dpy);
char text[255];
XEvent e;
KeySym key;
int numKeys = 0;
for(;;) {
XNextEvent(dpy, &e);
if(e.type == KeyPress) {
//With modifier XLookupString will return garbage(?) in text[0] and key as latin1
if((numKeys = XLookupString(&e.xkey, text, 255, &key, 0))) {
printf("lookup returned:\n");
for(int i = 0; i < numKeys; i++) {
printf("text[%d]=%x\n", i, text[i]);
}
if(e.xkey.state == ControlMask && key == XK_q) {
exitOnCondition(1, "C-Q pressed", 0, dpy, &w, &gc);
}
}
}
}
XFreeGC(dpy, gc);
XDestroyWindow(dpy,w);
XCloseDisplay(dpy);
}
Will this code correctly handle Ctrl-q event on any system?
Can I use e.xkey.state to check for Ctrl modifier after XLookupString for any keyboard layout even when Ctrl is rebound to CAPS Lock(or anything else)?
Why does XLookupString return one symbol text[0]==0x11 for Ctrl-q event and not text[0]==CtrlModifierCode text[1]=='q'?
XLookupString returns a sequence of ISO-8859-1 characters (at least according to the manual I have; I do not know how up-to-date it is). There is no character code in ISO-8859-1 for the "ctrl" key by itself. Strictly speaking, there isn't one for the ctrl-q combination either, but tradition dictates that ctrl + (A...Z) map to the 1...26 range.

What's going on in my custom list view control that leads to rows either partially or fully not being redrawn when scrolling?

I'm building a custom listview control as the standard comctl32.dll one does not adequately fit my needs. Things seem to be going well for what I have so far, except for scrolling.
At first, things look like this (under wine, but real Windows is affected too);
The form of corruption changes depending on the type of scrolling used. If I scroll using the mouse wheel, every few rows is corrupted:
And if I scroll by dragging the thumb, this happens:
In both cases, clicking somewhere will redraw everything, and the corruption goes away.
I'm not really sure what I'm doing wrong here.
Here's the code; I will explain how it works below:
// 19 october 2014
#define UNICODE
#define _UNICODE
#define STRICT
#define STRICT_TYPED_ITEMIDS
// get Windows version right; right now Windows XP
#define WINVER 0x0501
#define _WIN32_WINNT 0x0501
#define _WIN32_WINDOWS 0x0501 /* according to Microsoft's winperf.h */
#define _WIN32_IE 0x0600 /* according to Microsoft's sdkddkver.h */
#define NTDDI_VERSION 0x05010000 /* according to Microsoft's sdkddkver.h */
#include <windows.h>
#include <commctrl.h>
#include <stdint.h>
#include <uxtheme.h>
#include <string.h>
#include <wchar.h>
#include <windowsx.h>
#include <vsstyle.h>
#include <vssym32.h>
// #qo LIBS: user32 kernel32 gdi32 comctl32
// TODO
// - http://blogs.msdn.com/b/oldnewthing/archive/2003/09/09/54826.aspx (relies on the integrality parts? IDK)
// - might want to http://blogs.msdn.com/b/oldnewthing/archive/2003/09/17/54944.aspx instead
// - http://msdn.microsoft.com/en-us/library/windows/desktop/bb775574%28v=vs.85%29.aspx
// - hscroll (harder)
// - keyboard navigation
// - mousewheel navigation
#define tableWindowClass L"gouitable"
struct table {
HWND hwnd;
HFONT defaultFont;
HFONT font;
intptr_t selected;
intptr_t count;
intptr_t firstVisible;
intptr_t pagesize; // in rows
int wheelCarry;
HWND header;
int headerHeight;
intptr_t nColumns;
HIMAGELIST imagelist;
int imagelistHeight;
};
static LONG rowHeight(struct table *t)
{
HFONT thisfont, prevfont;
TEXTMETRICW tm;
HDC dc;
LONG ret;
dc = GetDC(t->hwnd);
if (dc == NULL)
abort();
thisfont = t->font; // in case WM_SETFONT happens before we return
prevfont = (HFONT) SelectObject(dc, thisfont);
if (prevfont == NULL)
abort();
if (GetTextMetricsW(dc, &tm) == 0)
abort();
if (SelectObject(dc, prevfont) != (HGDIOBJ) (thisfont))
abort();
if (ReleaseDC(t->hwnd, dc) == 0)
abort();
ret = tm.tmHeight;
if (ret < t->imagelistHeight)
ret = t->imagelistHeight;
return ret;
}
static void redrawAll(struct table *t)
{
if (InvalidateRect(t->hwnd, NULL, TRUE) == 0)
abort();
if (UpdateWindow(t->hwnd) == 0)
abort();
}
static RECT realClientRect(struct table *t)
{
RECT r;
if (GetClientRect(t->hwnd, &r) == 0)
abort();
r.top += t->headerHeight;
return r;
}
static void recomputeHScroll(struct table *t)
{
HDITEMW item;
intptr_t i;
int width = 0;
RECT r;
SCROLLINFO si;
// TODO count dividers
for (i = 0; i < t->nColumns; i++) {
ZeroMemory(&item, sizeof (HDITEMW));
item.mask = HDI_WIDTH;
if (SendMessageW(t->header, HDM_GETITEM, (WPARAM) i, (LPARAM) (&item)) == FALSE)
abort();
width += item.cxy;
}
if (GetClientRect(t->hwnd, &r) == 0)
abort();
ZeroMemory(&si, sizeof (SCROLLINFO));
si.cbSize = sizeof (SCROLLINFO);
si.fMask = SIF_PAGE | SIF_RANGE;
si.nPage = r.right - r.left;
si.nMin = 0;
si.nMax = width - 1; // - 1 because endpoints inclusive
SetScrollInfo(t->hwnd, SB_HORZ, &si, TRUE);
}
static void finishSelect(struct table *t)
{
if (t->selected < 0)
t->selected = 0;
if (t->selected >= t->count)
t->selected = t->count - 1;
// TODO update only the old and new selected items
redrawAll(t);
// TODO scroll to the selected item if it's not entirely visible
}
static void keySelect(struct table *t, WPARAM wParam, LPARAM lParam)
{
// TODO figure out correct behavior with nothing selected
if (t->count == 0) // don't try to do anything if there's nothing to do
return;
switch (wParam) {
case VK_UP:
t->selected--;
break;
case VK_DOWN:
t->selected++;
break;
case VK_PRIOR:
t->selected -= t->pagesize;
break;
case VK_NEXT:
t->selected += t->pagesize;
break;
case VK_HOME:
t->selected = 0;
break;
case VK_END:
t->selected = t->count - 1;
break;
default:
// don't touch anything
return;
}
finishSelect(t);
}
static void selectItem(struct table *t, WPARAM wParam, LPARAM lParam)
{
int x, y;
LONG h;
x = GET_X_LPARAM(lParam);
y = GET_Y_LPARAM(lParam);
h = rowHeight(t);
y += t->firstVisible * h;
y -= t->headerHeight;
y /= h;
t->selected = y;
if (t->selected >= t->count)
t->selected = -1;
finishSelect(t);
}
// TODO on initial show the items are not arranged properly
// TODO the lowest visible row does not redraw properly after scrolling
// TODO the row behind the header bar does not redraw properly after scrolling
static void vscrollto(struct table *t, intptr_t newpos)
{
SCROLLINFO si;
RECT scrollArea;
if (newpos < 0)
newpos = 0;
if (newpos > (t->count - t->pagesize))
newpos = (t->count - t->pagesize);
scrollArea = realClientRect(t);
// negative because ScrollWindowEx() is "backwards"
if (ScrollWindowEx(t->hwnd, 0, (-(newpos - t->firstVisible)) * rowHeight(t),
&scrollArea, &scrollArea, NULL, NULL,
SW_ERASE | SW_INVALIDATE) == ERROR)
abort();
t->firstVisible = newpos;
ZeroMemory(&si, sizeof (SCROLLINFO));
si.cbSize = sizeof (SCROLLINFO);
si.fMask = SIF_PAGE | SIF_POS | SIF_RANGE;
si.nPage = t->pagesize;
si.nMin = 0;
si.nMax = t->count - 1; // nMax is inclusive
si.nPos = t->firstVisible;
SetScrollInfo(t->hwnd, SB_VERT, &si, TRUE);
}
static void vscrollby(struct table *t, intptr_t n)
{
vscrollto(t, t->firstVisible + n);
}
static void wheelscroll(struct table *t, WPARAM wParam)
{
int delta;
int lines;
UINT scrollAmount;
delta = GET_WHEEL_DELTA_WPARAM(wParam);
if (SystemParametersInfoW(SPI_GETWHEELSCROLLLINES, 0, &scrollAmount, 0) == 0)
abort();
if (scrollAmount == WHEEL_PAGESCROLL)
scrollAmount = t->pagesize;
if (scrollAmount == 0) // no mouse wheel scrolling (or t->pagesize == 0)
return;
// the rest of this is basically http://blogs.msdn.com/b/oldnewthing/archive/2003/08/07/54615.aspx and http://blogs.msdn.com/b/oldnewthing/archive/2003/08/11/54624.aspx
// see those pages for information on subtleties
delta += t->wheelCarry;
lines = delta * ((int) scrollAmount) / WHEEL_DELTA;
t->wheelCarry = delta - lines * WHEEL_DELTA / ((int) scrollAmount);
vscrollby(t, -lines);
}
static void vscroll(struct table *t, WPARAM wParam)
{
SCROLLINFO si;
intptr_t newpos;
ZeroMemory(&si, sizeof (SCROLLINFO));
si.cbSize = sizeof (SCROLLINFO);
si.fMask = SIF_POS | SIF_TRACKPOS;
if (GetScrollInfo(t->hwnd, SB_VERT, &si) == 0)
abort();
newpos = t->firstVisible;
switch (LOWORD(wParam)) {
case SB_TOP:
newpos = 0;
break;
case SB_BOTTOM:
newpos = t->count - t->pagesize;
break;
case SB_LINEUP:
newpos--;
break;
case SB_LINEDOWN:
newpos++;
break;
case SB_PAGEUP:
newpos -= t->pagesize;
break;
case SB_PAGEDOWN:
newpos += t->pagesize;
break;
case SB_THUMBPOSITION:
newpos = (intptr_t) (si.nPos);
break;
case SB_THUMBTRACK:
newpos = (intptr_t) (si.nTrackPos);
}
vscrollto(t, newpos);
}
static void resize(struct table *t)
{
RECT r;
SCROLLINFO si;
HDLAYOUT headerlayout;
WINDOWPOS headerpos;
// do this first so our scrollbar calculations can be correct
if (GetClientRect(t->hwnd, &r) == 0) // use the whole client rect
abort();
headerlayout.prc = &r;
headerlayout.pwpos = &headerpos;
if (SendMessageW(t->header, HDM_LAYOUT, 0, (LPARAM) (&headerlayout)) == FALSE)
abort();
if (SetWindowPos(t->header, headerpos.hwndInsertAfter, headerpos.x, headerpos.y, headerpos.cx, headerpos.cy, headerpos.flags | SWP_SHOWWINDOW) == 0)
abort();
t->headerHeight = headerpos.cy;
// now adjust the scrollbars
r = realClientRect(t);
t->pagesize = (r.bottom - r.top) / rowHeight(t);
ZeroMemory(&si, sizeof (SCROLLINFO));
si.cbSize = sizeof (SCROLLINFO);
si.fMask = SIF_RANGE | SIF_PAGE;
si.nMin = 0;
si.nMax = t->count - 1;
si.nPage = t->pagesize;
SetScrollInfo(t->hwnd, SB_VERT, &si, TRUE);
recomputeHScroll(t);
}
static void drawItems(struct table *t, HDC dc, RECT cliprect)
{
HFONT thisfont, prevfont;
LONG height;
LONG y;
intptr_t i;
RECT controlSize; // for filling the entire selected row
intptr_t first, last;
POINT prevOrigin, prevViewportOrigin;
if (GetClientRect(t->hwnd, &controlSize) == 0)
abort();
height = rowHeight(t);
thisfont = t->font; // in case WM_SETFONT happens before we return
prevfont = (HFONT) SelectObject(dc, thisfont);
if (prevfont == NULL)
abort();
// adjust the clip rect and the window so that (0, 0) is always the first item
// adjust the viewport so that everything is shifted down t->headerHeight pixels
if (OffsetRect(&cliprect, 0, t->firstVisible * height) == 0)
abort();
if (GetWindowOrgEx(dc, &prevOrigin) == 0)
abort();
if (SetWindowOrgEx(dc, prevOrigin.x, prevOrigin.y + (t->firstVisible * height), NULL) == 0)
abort();
if (SetViewportOrgEx(dc, 0, t->headerHeight, &prevViewportOrigin) == 0)
abort();
// see http://blogs.msdn.com/b/oldnewthing/archive/2003/07/29/54591.aspx and http://blogs.msdn.com/b/oldnewthing/archive/2003/07/30/54600.aspx
first = cliprect.top / height;
if (first < 0)
first = 0;
last = (cliprect.bottom + height - 1) / height;
if (last >= t->count)
last = t->count;
y = first * height;
for (i = first; i < last; i++) {
RECT rsel;
HBRUSH background;
int textColor;
WCHAR msg[100];
RECT headeritem;
intptr_t j;
LRESULT xoff;
// TODO verify these two
background = (HBRUSH) (COLOR_WINDOW + 1);
textColor = COLOR_WINDOWTEXT;
if (t->selected == i) {
// these are the colors wine uses (http://source.winehq.org/source/dlls/comctl32/listview.c)
// the two for unfocused are also suggested by http://stackoverflow.com/questions/10428710/windows-forms-inactive-highlight-color
background = (HBRUSH) (COLOR_HIGHLIGHT + 1);
textColor = COLOR_HIGHLIGHTTEXT;
if (GetFocus() != t->hwnd) {
background = (HBRUSH) (COLOR_BTNFACE + 1);
textColor = COLOR_BTNTEXT;
}
}
// first fill the selection rect
rsel.left = controlSize.left;
rsel.top = y;
rsel.right = controlSize.right - controlSize.left;
rsel.bottom = y + height;
if (FillRect(dc, &rsel, background) == 0)
abort();
xoff = SendMessageW(t->header, HDM_GETBITMAPMARGIN, 0, 0);
// now draw the cells
if (SetTextColor(dc, GetSysColor(textColor)) == CLR_INVALID)
abort();
if (SetBkMode(dc, TRANSPARENT) == 0)
abort();
for (j = 0; j < t->nColumns; j++) {
if (SendMessageW(t->header, HDM_GETITEMRECT, (WPARAM) j, (LPARAM) (&headeritem)) == 0)
abort();
if (j == 1) { // TODO
IMAGELISTDRAWPARAMS ip;
ZeroMemory(&ip, sizeof (IMAGELISTDRAWPARAMS));
ip.cbSize = sizeof (IMAGELISTDRAWPARAMS);
ip.himl = t->imagelist;
ip.i = 0;
ip.hdcDst = dc;
ip.x = headeritem.left + xoff;
ip.y = y;
ip.cx = 0; // draw whole image
ip.cy = 0;
ip.xBitmap = 0;
ip.yBitmap = 0;
ip.rgbBk = CLR_NONE;
ip.fStyle = ILD_NORMAL | ILD_SCALE; // TODO alpha-blend; ILD_DPISCALE?
// TODO ILS_ALPHA?
if (ImageList_DrawIndirect(&ip) == 0)
abort();
continue;
}
rsel.left = headeritem.left + xoff;
rsel.top = y;
rsel.right = headeritem.right;
rsel.bottom = y + height;
// TODO vertical center in case the height is less than the icon height?
if (DrawTextExW(dc, msg, wsprintf(msg, L"Item %d", i), &rsel, DT_END_ELLIPSIS | DT_LEFT | DT_NOPREFIX | DT_SINGLELINE, NULL) == 0)
abort();
}
y += height;
}
// reset everything
if (SetViewportOrgEx(dc, prevViewportOrigin.x, prevViewportOrigin.y, NULL) == 0)
abort();
if (SetWindowOrgEx(dc, prevOrigin.x, prevOrigin.y, NULL) == 0)
abort();
if (SelectObject(dc, prevfont) != (HGDIOBJ) (thisfont))
abort();
}
static LRESULT CALLBACK tableWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
struct table *t;
HDC dc;
PAINTSTRUCT ps;
NMHDR *nmhdr = (NMHDR *) lParam;
NMHEADERW *nm = (NMHEADERW *) lParam;
t = (struct table *) GetWindowLongPtrW(hwnd, GWLP_USERDATA);
if (t == NULL) {
// we have to do things this way because creating the header control will fail mysteriously if we create it first thing
// (which is fine; we can get the parent hInstance this way too)
if (uMsg == WM_NCCREATE) {
CREATESTRUCTW *cs = (CREATESTRUCTW *) lParam;
t = (struct table *) malloc(sizeof (struct table));
if (t == NULL)
abort();
ZeroMemory(t, sizeof (struct table));
t->hwnd = hwnd;
// TODO this should be a global
t->defaultFont = (HFONT) GetStockObject(SYSTEM_FONT);
if (t->defaultFont == NULL)
abort();
t->font = t->defaultFont;
t->selected = 5;t->count=100;//TODO
t->header = CreateWindowExW(0,
WC_HEADERW, L"",
// TODO is HOTTRACK needed?
WS_CHILD | HDS_FULLDRAG | HDS_HORZ | HDS_HOTTRACK,
0, 0, 0, 0,
t->hwnd, (HMENU) 100, cs->hInstance, NULL);
if (t->header == NULL)
abort();
{HDITEMW item;
ZeroMemory(&item, sizeof (HDITEMW));
item.mask = HDI_WIDTH | HDI_TEXT | HDI_FORMAT;
item.cxy = 200;
item.pszText = L"Column";
item.fmt = HDF_LEFT | HDF_STRING;
if (SendMessage(t->header, HDM_INSERTITEM, 0, (LPARAM) (&item)) == (LRESULT) (-1))
abort();
ZeroMemory(&item, sizeof (HDITEMW));
item.mask = HDI_WIDTH | HDI_TEXT | HDI_FORMAT;
item.cxy = 150;
item.pszText = L"Column 2";
item.fmt = HDF_LEFT | HDF_STRING;
if (SendMessage(t->header, HDM_INSERTITEM, 1, (LPARAM) (&item)) == (LRESULT) (-1))
abort();
t->nColumns=2;
t->imagelist = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), ILC_COLOR32, 1, 1);
if(t->imagelist==NULL)abort();
{
HICON icon;
int unused;
icon = LoadIconW(NULL, IDI_ERROR);
if(icon == NULL)abort();
if (ImageList_AddIcon(t->imagelist, icon) == -1)abort();
if (ImageList_GetIconSize(t->imagelist, &unused, &(t->imagelistHeight)) == 0)abort();
}
}
SetWindowLongPtrW(hwnd, GWLP_USERDATA, (LONG_PTR) t);
}
// even if we did the above, fall through
return DefWindowProcW(hwnd, uMsg, wParam, lParam);
}
switch (uMsg) {
case WM_PAINT:
dc = BeginPaint(hwnd, &ps);
if (dc == NULL)
abort();
drawItems(t, dc, ps.rcPaint);
EndPaint(hwnd, &ps);
return 0;
case WM_SETFONT:
t->font = (HFONT) wParam;
if (t->font == NULL)
t->font = t->defaultFont;
// also set the header font
SendMessageW(t->header, WM_SETFONT, wParam, lParam);
if (LOWORD(lParam) != FALSE) {
// the scrollbar page size will change so redraw that too
// also recalculate the header height
// TODO do that when this is FALSE too somehow
resize(t);
redrawAll(t);
}
return 0;
case WM_GETFONT:
return (LRESULT) t->font;
case WM_VSCROLL:
vscroll(t, wParam);
return 0;
case WM_MOUSEWHEEL:
wheelscroll(t, wParam);
return 0;
case WM_SIZE:
resize(t);
return 0;
case WM_LBUTTONDOWN:
selectItem(t, wParam, lParam);
return 0;
case WM_SETFOCUS:
case WM_KILLFOCUS:
// all we need to do here is redraw the highlight
// TODO localize to just the selected item
// TODO ensure giving focus works right
redrawAll(t);
return 0;
case WM_KEYDOWN:
keySelect(t, wParam, lParam);
return 0;
// TODO header double-click
case WM_NOTIFY:
if (nmhdr->hwndFrom == t->header)
switch (nmhdr->code) {
// I could use HDN_TRACK but wine doesn't emit that
case HDN_ITEMCHANGING:
case HDN_ITEMCHANGED: // TODO needed?
recomputeHScroll(t);
redrawAll(t);
return FALSE;
}
// otherwise fall through
default:
return DefWindowProcW(hwnd, uMsg, wParam, lParam);
}
abort();
return 0; // unreached
}
void makeTableWindowClass(void)
{
WNDCLASSW wc;
ZeroMemory(&wc, sizeof (WNDCLASSW));
wc.lpszClassName = tableWindowClass;
wc.lpfnWndProc = tableWndProc;
wc.hCursor = LoadCursorW(NULL, IDC_ARROW);
wc.hIcon = LoadIconW(NULL, IDI_APPLICATION);
wc.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1); // TODO correct?
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.hInstance = GetModuleHandle(NULL);
if (RegisterClassW(&wc) == 0)
abort();
}
int main(void)
{
HWND mainwin;
MSG msg;
INITCOMMONCONTROLSEX icc;
ZeroMemory(&icc, sizeof (INITCOMMONCONTROLSEX));
icc.dwSize = sizeof (INITCOMMONCONTROLSEX);
icc.dwICC = ICC_LISTVIEW_CLASSES;
if (InitCommonControlsEx(&icc) == 0)
abort();
makeTableWindowClass();
mainwin = CreateWindowExW(0,
tableWindowClass, L"Main Window",
WS_OVERLAPPEDWINDOW | WS_HSCROLL | WS_VSCROLL,
CW_USEDEFAULT, CW_USEDEFAULT,
400, 400,
NULL, NULL, GetModuleHandle(NULL), NULL);
if (mainwin == NULL)
abort();
ShowWindow(mainwin, SW_SHOWDEFAULT);
if (UpdateWindow(mainwin) == 0)
abort();
while (GetMessageW(&msg, NULL, 0, 0) > 0) {
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
return 0;
}
t->firstVisible is the first visible row; t->pagesize is the number of rows per vscroll page, and t->headerHeight is the height of the header control in pixels. t->wheelcarry is the mouse wheel carryover, from this.
The actual height of a row is calculated by the rowHeight() function, which vscrollto(), the routine that actually calls ScrolLWindow(), multiplies the new t->firstVisible by to figure out how many pixels to scroll.
The realClientRect() logic in vscrollto() is to make sure that only the content area gets scrolled, and not the header control.
For drawing, I change the window origin to make (0, 0) be the first item (row 0) no matter what and then I change the viewport origin to account for the header. Readers of MSDN will note that this sounds strange (the documentation for both SetWindowOrgEx() and SetViewportOrgEx() say you generally don't use both), but my attempts at accounting for the header height manually have proven disastrous.
Again, this happens on both real Windows (did this earlier today on Windows 7) and wine.
What did I do wrong? Thanks.
I've had a play around with your code and I think the problem is definitely caused by your modifying the viewport and window origins. The issue is that adjusting the origins like that changes where new drawing will appear in the window, but it doesn't cause the update region to be shifted as well.
Therefore when there's only a partially invalidated area, as you get when scrolling, some of your drawing is clipped out by the update region.
It's actually not specific to scrolling - you can also see the same effect by dragging your window off the bottom of the screen, and then slowly dragging it back into view. The window will end up completely blank. If you comment out your call to SetViewportOrgEx the problem goes away completely.
You could probably solve this by adjusting the clipping region as well - you'd have to get the current update region, shift it by the same offset, and reselect it into the DC. Alternatively, you could redesign your painting code to take the height of the header into account when painting rather than shifting the viewport offset. You mentioned you had other issues when doing this - I suspect it will be easier to solve those than you think (i.e. it seems like a bit of an XY problem).

How to upload 32 bit image to server-side pixmap

I'm trying to create server-side RGBA pixmap from client side buffer. CreatePixmap & CreateImage work ok for 32 and 24 bit, but XPutImage result in Match Error returned by server
X Error of failed request: BadMatch (invalid parameter attributes)
Major opcode of failed request: 72 (X_PutImage)
Serial number of failed request: 8
Current serial number in output stream: 8
server does support 32 bit pixmaps (xdpyinfo output: https://gist.github.com/2582961). Same behaviour on ubuntu 12.04 (X.Org version: 1.11.3) and OSX with X.app (X.Org version: 1.10.3)
Why following code fails?
#include <stdlib.h>
#include <X11/Xlib.h>
int main(int argc, char **argv)
{
int width = 100;
int height = 100;
int depth = 32; // works fine with depth = 24
int bitmap_pad = 32; // 32 for 24 and 32 bpp, 16, for 15&16
int bytes_per_line = 0; // number of bytes in the client image between the start of one scanline and the start of the next
Display *display=XOpenDisplay(0);
unsigned char *image32=(unsigned char *)malloc(width*height*4);
XImage *img = XCreateImage(display, CopyFromParent, depth, ZPixmap, 0, image32, width, height, bitmap_pad, bytes_per_line);
Pixmap p = XCreatePixmap(display, XDefaultRootWindow(display), width, height, depth);
XPutImage(display, p, DefaultGC(display, 0), img, 0, 0, 0, 0, width, height); // 0, 0, 0, 0 are src x,y and dst x,y
XEvent ev;
while (1) {
XNextEvent(display, &ev);
}
}
Update: It looks like I finally got answer: use GC associated with pixmap instead of DefaultGC (which has depth of root window)
#include <stdlib.h>
#include <X11/Xlib.h>
int main(int argc, char **argv)
{
int width = 100;
int height = 100;
int depth = 32; // works fine with depth = 24
int bitmap_pad = 32; // 32 for 24 and 32 bpp, 16, for 15&16
int bytes_per_line = 0; // number of bytes in the client image between the start of one scanline and the start of the next
Display *display=XOpenDisplay(0);
unsigned char *image32=(unsigned char *)malloc(width*height*4);
XImage *img = XCreateImage(display, CopyFromParent, depth, ZPixmap, 0, image32, width, height, bitmap_pad, bytes_per_line);
Pixmap p = XCreatePixmap(display, XDefaultRootWindow(display), width, height, depth);
XGCValues gcvalues;
GC gc = XCreateGC(display, p, 0, &gcvalues);
XPutImage(display, p, gc, img, 0, 0, 0, 0, width, height); // 0, 0, 0, 0 are src x,y and dst x,y
XEvent ev;
while (1) {
XNextEvent(display, &ev);
}
}
The problem is with DefaultGC() which return a GC with bit depth of system default screen. If you look at line 53 of your gist paste you see that this is 24:
depth of root window: 24 planes
On line 63 you see that it uses 0x22 as default which is shown in more detail in line 64 to 70:
visual:
visual id: 0x22
class: TrueColor
depth: 24 planes
available colormap entries: 256 per subfield
red, green, blue masks: 0xff0000, 0xff00, 0xff
significant bits in color specification: 8 bits
You could probably do this a bit nicer but as a start you can try this:
Note: This uses system visuals so most probably only support depth of 24 or 32.
#include <stdio.h>
#include <stdlib.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#ifdef DEBUG
int dbg = 1;
#else
int dbg = 0;
#endif
/* Return a GC based on depth */
int gc_depth(int depth, Display *dpy, Window scr, Window root, GC *gc)
{
Window win;
Visual *visual;
XVisualInfo vis_info;
XSetWindowAttributes win_attr;
unsigned long win_mask;
if(!XMatchVisualInfo(dpy, scr, depth, TrueColor, &vis_info)) {
fprintf(stderr,
" * ERR: %d depth not supported\n",
depth
);
return 1;
}
visual = vis_info.visual;
win_attr.colormap = XCreateColormap(dpy, root, visual, AllocNone);
win_attr.background_pixel = 0;
win_attr.border_pixel = 0;
win_mask = CWBackPixel | CWColormap | CWBorderPixel;
win = XCreateWindow(
dpy, root,
0, 0,
100, 100, /* dummy size */
0, depth,
InputOutput, visual,
win_mask, &win_attr);
/* To flush out any errors */
if (dbg) XSync(dpy, True);
*gc = XCreateGC(dpy, win, 0, 0);
if (dbg) XSync(dpy, True);
XDestroyWindow(dpy, win);
if (dbg) XSync(dpy, True);
return 0;
}
int main(void)
{
int w = 100;
int h = 100;
int depth = 32;
int bitmap_pad = 32;
int bpl = 0;
Display *dpy;
Window root;
Window scr;
GC gc;
int root_depth;
Pixmap pm;
XImage *img;
unsigned char *buf_img;
if(!(dpy = XOpenDisplay(NULL))) {
fprintf(stderr,
" * ERR: Failed to open display.\n");
return 1;
}
#ifdef DEBUG
/* To get errors in order, slows down
* One can also define int _Xdebug = 1;
* */
XSynchronize(dpy, True);
#endif
root = XDefaultRootWindow(dpy);
scr = XDefaultScreen(dpy);
if ((buf_img = malloc(w * h * 4)) == NULL) {
fprintf(stderr,
" * ERR: Unable to alloacte %d bytes\n",
w * h * 4);
return 1;
}
root_depth = DefaultDepth(dpy, scr);
fprintf(stderr,
"Default depth: %d\n",
root_depth);
/* This should be doen more nice */
if (depth != root_depth) {
if (gc_depth(depth, dpy, scr, root, &gc) != 0)
return 1;
} else {
gc = DefaultGC(dpy, 0);
}
img = XCreateImage(
dpy, CopyFromParent,
depth, ZPixmap,
0, (char *)buf_img,
w, h,
bitmap_pad, bpl);
/* To flush out any errors */
if (dbg) XSync(dpy, True);
pm = XCreatePixmap(
dpy, root,
w, h,
depth);
if (dbg) XSync(dpy, True);
XPutImage(
dpy, pm,
gc, img,
0, 0,
0, 0,
w, h);
if (dbg) XSync(dpy, True);
XFreePixmap(dpy, pm);
XDestroyImage(img);
XFreeGC(dpy, gc);
if (dbg) XSync(dpy, True);
fprintf(stderr,
"OK!\n");
return 0;
}
Well, your code works for 32 bits images if you just create a GC passing a drawable on argument which is 32 bits. XCreateGC(dpy, drawable, 0, 0), where drawable can be a pixmap with 32 bits depth. It works perfect with me.

Resources