I am currently working on a cross-host clipboard sharing tool that can share text, rich text, or files.
Based on what I've learned so far, I already know which X11 events need to be handled, but I can't listen to them.
My current code I can only catch events that select text.
how to get the clipboard change event caused by ctrl+c?
#include <QCoreApplication>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <xcb/xcb.h>
#include <xcb/xfixes.h>
/**
* Enumeration of standard X11 atom identifiers
*/
typedef enum std_x_atoms {
/** The TARGETS atom identifier **/
X_ATOM_TARGETS = 0,
/** The MULTIPLE atom identifier **/
X_ATOM_MULTIPLE,
/** The TIMESTAMP atom identifier **/
X_ATOM_TIMESTAMP,
/** The INCR atom identifier **/
X_ATOM_INCR,
/** The CLIPBOARD atom identifier **/
X_ATOM_CLIPBOARD,
/** The UTF8_STRING atom identifier **/
X_ATOM_UTF8_STRING,
X_ATOM_XCLIPD,
/** End marker sentinel **/
X_ATOM_END
} std_x_atoms;
/**
* Union to simplify getting interned atoms from XCB
*/
typedef union atom_c {
/** The atom **/
xcb_atom_t atom;
/** The cookie returned by xcb_intern_atom **/
xcb_intern_atom_cookie_t cookie;
} atom_c;
/**
* The standard atom names. These values should match the
* std_x_atoms enumeration.
*/
const char * const g_std_atom_names[X_ATOM_END] = {
"TARGETS", "MULTIPLE", "TIMESTAMP", "INCR",
"CLIPBOARD", "UTF8_STRING", "XCLIPD"
};
atom_c std_atoms[X_ATOM_END];
/**
* \brief Interns the list of atoms
*
* \param [in] xc The XCB connection.
* \param [out] atoms The location to store interned atoms.
* \param [in] atom_names The names of the atoms to intern.
* \param [in] number The number of atoms to intern.
* \return true iff all atoms were interned.
*/
static bool x11_intern_atoms(xcb_connection_t *xc, atom_c *atoms, const char * const *atom_names, int number) {
for (int i = 0; i < number; i++) {
atoms[i].cookie = xcb_intern_atom(xc, 0,
strlen(atom_names[i]), atom_names[i]);
}
for (int i = 0; i < number; i++) {
xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(xc,
atoms[i].cookie, NULL);
if (reply == NULL) {
return false;
}
atoms[i].atom = reply->atom;
free(reply); /* XCB: Do not use custom allocators */
}
return true;
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
xcb_connection_t *c;
xcb_screen_t *screen;
xcb_window_t win;
xcb_generic_event_t *e;
uint32_t mask = 0;
uint32_t values[2];
/* Create the window */
c = xcb_connect (NULL, NULL);
screen = xcb_setup_roots_iterator (xcb_get_setup (c)).data;
if (!x11_intern_atoms(c, std_atoms, g_std_atom_names, X_ATOM_END))
return 1;
// xcb_flush (c);
win = xcb_generate_id (c);
mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK;
values[0] = screen->white_pixel;
values[1] = XCB_EVENT_MASK_PROPERTY_CHANGE | XCB_EVENT_MASK_STRUCTURE_NOTIFY;;
xcb_create_window (c, /* Connection */
XCB_COPY_FROM_PARENT, /* depth */
win, /* window Id */
screen->root, /* parent window */
0, 0, /* x, y */
150, 150, /* width, height */
10, /* border_width */
XCB_WINDOW_CLASS_INPUT_OUTPUT, /* class */
screen->root_visual, /* visual */
mask, values); /* masks */
xcb_map_window (c, win);
xcb_flush (c);
// init xfixes
xcb_generic_error_t *error = 0;
const xcb_query_extension_reply_t *reply = xcb_get_extension_data(c, &xcb_xfixes_id);
if (!reply || !reply->present) {
return -1;
}
xcb_xfixes_query_version_cookie_t xfixes_query_cookie = xcb_xfixes_query_version(c,
XCB_XFIXES_MAJOR_VERSION,
XCB_XFIXES_MINOR_VERSION);
xcb_xfixes_query_version_reply_t *xfixes_query = xcb_xfixes_query_version_reply (c,
xfixes_query_cookie, &error);
if (!xfixes_query || error || xfixes_query->major_version < 2) {
free(error);
}
free(xfixes_query);
// delivers request
mask = XCB_XFIXES_SELECTION_EVENT_MASK_SELECTION_CLIENT_CLOSE
| XCB_XFIXES_SELECTION_EVENT_MASK_SELECTION_WINDOW_DESTROY
| XCB_XFIXES_SELECTION_EVENT_MASK_SET_SELECTION_OWNER;
xcb_xfixes_select_selection_input_checked(c, win, XCB_ATOM_NONE, mask);
xcb_flush(c);
// recevie events
uint response_type;
while (e = xcb_wait_for_event(c)) {
xcb_xfixes_selection_notify_event_t *notify_event = reinterpret_cast<xcb_xfixes_selection_notify_event_t *>(e);
printf("response_type = %d\n", response_type);
response_type = notify_event->response_type & ~0x80;
printf("response_type = %d\n", response_type);
if (response_type == reply->first_event + XCB_XFIXES_SELECTION_NOTIFY) {
printf("notify\n");
} else {
printf("code:%d\n", response_type);
}
}
return a.exec();
}
Related
I am in a project where map and other information are displayed in a Gtk window. There are several map layers, that I draw into a Cairo surface and save in a .png (plotRect() function in code below). That .png is displayed in a Gtk image when there is a Gtk draw signal (draw()).
I now want to accomplish smooth grabbing and panning. When the mouse button is pressed, I want the whole image to be translated within the window area, follow the movements of the mouse. When the button is released the image should be Cairo remade and redrawn with new bounds. During the drag/pan procedure itself there is no need to draw areas that where previously out of the window borders -- it is OK to wait for that to be done when the mouse button is released.
Enclosed you find a simplified version of my code. The main has a loop going until the windows is closed, redrawing the image after each panning. The problem is in the pan() function. After the translation in line 21 I woould expect the draw statement in line 128 to successively draw panned images while moving the mouse, but the visible image is unaffected. Uncommenting line 23 shows that graph->image has really been modified, and I can see that the draw signal of line 25 is invoking the draw() callback function. After button release, the translated image is correctly displayed.
Can anyone please give me some advice?
I'm using gcc, Cairo, Gtk3 and Ubuntu 18.04 on a double-booted MacBook Pro 64 bit i5.
#include <cairo.h>
#include <chrono>
#include <cmath>
#include <gtk/gtk.h>
#include <iostream>
#include <mutex>
#include <thread>
using namespace std;
mutex mtx;
bool gtkMainLoopRunning = false;
#define SLEEP(d) this_thread::sleep_for(chrono::milliseconds(d))
template <class T>
inline T sqr(T x) {
return x * x;
}
//-----------------------------------------------------------------------------
class Graph {
double toplat, leftlon; // upper left corner
double dydlat, dxdlon; // pixels / degree lon/lat
public:
int size; // window x = y
GtkWidget *window;
GtkImage *image;
const char *png = "/tmp/image.png";
cairo_surface_t *surface{};
cairo_t *cr{};
bool closed = false;
bool leftbuttondown = false;
int mousex = 0, mousey = 0;
Graph(const double, const double, const double, const double);
~Graph();
void plotRect(const double, const double, const double, const double);
bool pan();
};
//-----------------------------------------------------------------------------
static gboolean draw(GtkWidget *widget, cairo_t *cr, gpointer data) {
Graph *graph = (Graph *)data;
if (!graph->leftbuttondown) {
mtx.lock();
gtk_image_set_from_file(graph->image, graph->png);
mtx.unlock();
}
return FALSE;
}
//-----------------------------------------------------------------------------
static gboolean clicked(GtkWidget *widget, GdkEventButton *button, gpointer data) {
Graph *graph = (Graph *)data;
if (button->button == 1) {
if (button->type == GDK_BUTTON_PRESS) {
graph->leftbuttondown = true;
} else if (button->type == GDK_BUTTON_RELEASE) {
graph->leftbuttondown = false;
}
}
graph->mousex = button->x;
graph->mousey = button->y;
return FALSE;
}
//-----------------------------------------------------------------------------
Graph::~Graph() {
do {
SLEEP(100);
} while (gtkMainLoopRunning); // wait until gtk main loop has stopped
cairo_destroy(cr);
cairo_surface_destroy(surface);
}
//-----------------------------------------------------------------------------
void destroyWindow(GtkWidget *widget, gpointer data) {
gtk_main_quit();
gtkMainLoopRunning = false;
Graph *graph = (Graph *)data;
graph->closed = true; // signal gtkThread to finish
gtk_widget_destroy((GtkWidget *)graph->image);
gtk_widget_destroy(graph->window);
}
//-----------------------------------------------------------------------------
Graph::Graph(const double minlat, const double minlon, const double maxlat, const double maxlon) {
gtk_init(NULL, NULL);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
size = 800;
gtk_widget_show(window);
image = (GtkImage *)gtk_image_new();
gtk_widget_set_size_request((GtkWidget *)image, size, size);
gtk_container_add(GTK_CONTAINER(window), (GtkWidget *)image);
gtk_widget_show((GtkWidget *)image);
g_signal_connect(image, "draw", G_CALLBACK(draw), this);
g_signal_connect(window, "destroy", G_CALLBACK(destroyWindow), this);
surface = cairo_image_surface_create(CAIRO_FORMAT_RGB24, size, size);
cr = cairo_create(surface);
gtk_widget_add_events(window, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_BUTTON1_MOTION_MASK);
g_signal_connect(window, "button-press-event", G_CALLBACK(clicked), this);
g_signal_connect(window, "button-release-event", G_CALLBACK(clicked), this);
g_signal_connect(window, "motion-notify-event", G_CALLBACK(clicked), this);
const double coslat = cos((minlat + maxlat) / 2 * M_PI / 180);
const double extension = max(maxlat - minlat, (maxlon - minlon) * coslat); // [lat degrees]
toplat = (minlat + maxlat + extension) / 2;
leftlon = (minlon + maxlon - extension / coslat) / 2;
dydlat = -size / extension; // [pixels/degree]
dxdlon = size / extension * coslat;
gtkMainLoopRunning = true;
thread(gtk_main).detach();
}
//-----------------------------------------------------------------------------
bool Graph::pan() {
const int sqrSignifPan = sqr(4);
while (!closed) {
if (leftbuttondown) {
int x0 = mousex;
int y0 = mousey;
int dx = 0, dy = 0;
GdkPixbuf *origPixbuf = gdk_pixbuf_new_from_file("/tmp/image.png", NULL);
char *origPixels = (char *)gdk_pixbuf_get_pixels(origPixbuf);
const int rowstride = gdk_pixbuf_get_rowstride(origPixbuf);
const int nChannels = gdk_pixbuf_get_n_channels(origPixbuf);
char *imagePixels = (char *)gdk_pixbuf_get_pixels(gtk_image_get_pixbuf(image));
while (leftbuttondown) {
const int dx0 = dx, dy0 = dy;
dx = mousex - x0, dy = mousey - y0;
if (sqr(dx - dx0) + sqr(dy - dy0) >= sqrSignifPan) {
const int minx = max(0, -dx);
const int nx = max(0, size - abs(dx));
if (nx > 0) {
for (int y = max(0, -dy); y < min(size, size - dy); ++y) {
memcpy(imagePixels + (y + dy) * rowstride + (minx + dx) * nChannels, origPixels + y * rowstride + minx * nChannels, nx * nChannels);
}
// gdk_pixbuf_save(gtk_image_get_pixbuf(image), "/tmp/imagePixbuf.png", "png", NULL, NULL);
gtk_widget_queue_draw((GtkWidget *)image);
SLEEP(10); // pause for drawing
}
}
SLEEP(100);
}
// rescale graph
toplat -= (mousey - y0) / dydlat;
leftlon -= (mousex - x0) / dxdlon;
dxdlon = -dydlat * cos((toplat + size / dydlat / 2) * M_PI / 180);
gtk_widget_queue_draw((GtkWidget *)image);
return true;
}
SLEEP(100);
}
return false;
}
//-----------------------------------------------------------------------------
void Graph::plotRect(const double minlat, const double minlon, const double maxlat, const double maxlon) {
cairo_set_source_rgb(cr, 1, 1, 1);
cairo_paint(cr);
cairo_rectangle(cr, (minlon - leftlon) * dxdlon, (minlat - toplat) * dydlat, (maxlon - minlon) * dxdlon, (maxlat - minlat) * dydlat);
cairo_set_source_rgb(cr, 0, 1, 0);
cairo_fill(cr);
mtx.lock();
remove(png);
cairo_surface_write_to_png(surface, png);
mtx.unlock();
}
//-----------------------------------------------------------------------------
int main() {
const double minlat = 59, minlon = 16, maxlat = 60, maxlon = 18;
Graph *graph = new Graph(minlat - 0.5, minlon - 1, maxlat + 0.5, maxlon + 1);
do {
graph->plotRect(minlat, minlon, maxlat, maxlon);
} while (graph->pan());
delete graph;
}
With a friend I am working on a tetris like game within a terminal. We are using ncurses to manage it and we have different "views". For example a view manages the grid while the other manages the Menu. However we have a problem: when we start the game, we need to press a key in order to see it, otherwise the window is empty.
Here is the code ran when we create the menu (the constructor):
MenuScreen::MenuScreen()
{
if (has_colors())
{
use_default_colors();
start_color();
init_pair(WHITEONRED, COLOR_WHITE, COLOR_RED);
init_pair(WHITEONBLUE, COLOR_WHITE, COLOR_BLUE);
init_pair(REDONWHITE, COLOR_RED, COLOR_WHITE);
}
char *choices[] = /* The menu choices */
{
(char *)" Solo ",
(char *)" Bot ",
(char *)" Tetrix ",
(char *)" Quitter ",
NULL};
/* Calculate nchoices */
for (n_choices = 0; choices[n_choices]; n_choices++)
;
/* alloction of an iteam array for the menu */
my_items = (ITEM **)calloc(n_choices + 1, sizeof(ITEM *));
for (ssChoice = 0; ssChoice < n_choices; ++ssChoice)
my_items[ssChoice] = new_item(choices[ssChoice], NULL);
my_items[n_choices] = (ITEM *)NULL;
/* menu structure creation */
my_menu = new_menu((ITEM **)my_items);
/* symbole on the left of selected iteam*/
set_menu_mark(my_menu, "> ");
/* Windows Border cration */
wBorder = newwin(8, 40, LINES / 2 - 8 / 2, COLS / 2 - 40 / 2);
registerWindow(wBorder);
wattrset(wBorder, COLOR_PAIR(WHITEONRED));
windowsFilling(wBorder);
box(wBorder, 0, 0);
windowsBorderTitle(wBorder, " option ");
wUI = derwin(wBorder, 8 - 2, 40 - 2, 2, 2);
registerWindow(wUI);
set_menu_sub(my_menu, wUI);
set_menu_fore(my_menu, COLOR_PAIR(REDONWHITE));
set_menu_back(my_menu, COLOR_PAIR(WHITEONRED));
/* menu display */
post_menu(my_menu);
}
And when the next key is pressed:
void MenuScreen::next()
{
menu_driver(my_menu, REQ_DOWN_ITEM);
update();
}
And here is what update() contains:
void MenuScreen::update()
{
touchwin(wUI);
wrefresh(wUI);
touchwin(wBorder);
wrefresh(wBorder);
}
If we remove update() from the next() function, the menu is no longer displayed when we press the next key, so we tried to add a call to update() at the end of the constructor, but this didn't work. We have no idea what could cause this bug. Here is our entire source code:
https://github.com/Th0rgal/poyuterm/tree/22e2422930a29fbe83414e382fd566c3eac69366
EDIT: Here is a minimal reproducible example made by my friend:
To build it, copy paste it in a menu.cpp file and type: g++ menu.cpp -o menu -lncurses -l menu
#include <stdlib.h> /* calloc() */
#include <string.h> /* strlen() */
#include <ncurses.h>
#include <menu.h>
#include <curses.h>
#define WHITEONRED 10
#define WHITEONBLUE 20
#define WHITEONBLACK 30
#define BLACKONWHITE 40
#define REDONWHITE 50
void windowsBorderTitle(WINDOW *pwin, const char *title)
{
int x, maxy, maxx, stringsize;
getmaxyx(pwin, maxy, maxx);
stringsize = 4 + strlen(title);
x = (maxx - stringsize) / 2;
mvwaddch(pwin, 0, x, ACS_RTEE);
waddch(pwin, ' ');
waddstr(pwin, title);
waddch(pwin, ' ');
waddch(pwin, ACS_LTEE);
}
void windowsFilling(WINDOW *pwin)
{
int y, x, maxy, maxx;
getmaxyx(pwin, maxy, maxx);
for (y = 0; y < maxy; y++)
for (x = 0; x < maxx; x++)
mvwaddch(pwin, y, x, ' ');
}
int main(int argc, char const *argv[])
{
initscr(); /* start ncurses */
cbreak(); /* immediately acquire each keystroke */
noecho(); /* do not echo user keystrokes */
keypad(stdscr, TRUE); /* enable detection of function keys */
int c;
ITEM **my_items;
MENU *my_menu;
WINDOW *wUI;
WINDOW *wBorder;
int n_choices;
int ssChoice;
int my_choice = -1;
if (has_colors())
{
use_default_colors();
start_color();
init_pair(WHITEONRED, COLOR_WHITE, COLOR_RED);
init_pair(WHITEONBLUE, COLOR_WHITE, COLOR_BLUE);
init_pair(REDONWHITE, COLOR_RED, COLOR_WHITE);
}
char *choices[] = /* The menu choices */
{
(char *)" Solo ",
(char *)" Bot ",
(char *)" Tetrix ",
(char *)" Quitter ",
NULL};
for(n_choices=0; choices[n_choices]; n_choices++);
/* ALLOCATE ITEM ARRAY AND INDIVIDUAL ITEMS */
my_items = (ITEM **)calloc(n_choices + 1, sizeof(ITEM *));
for(ssChoice = 0; ssChoice < n_choices; ++ssChoice)
my_items[ssChoice] = new_item(choices[ssChoice], NULL);
my_items[n_choices] = (ITEM *)NULL;
/* CREATE THE MENU STRUCTURE */
my_menu = new_menu((ITEM **)my_items);
/* PUT > TO THE LEFT OF HIGHLIGHTED ITEM */
set_menu_mark(my_menu, "> ");
/* SET UP WINDOW FOR MENU'S BORDER */
wBorder = newwin(16, 40, 2, 20);
wattrset(wBorder, COLOR_PAIR(WHITEONRED) | WA_BOLD);
windowsFilling(wBorder);
box(wBorder, 0, 0);
windowsBorderTitle(wBorder, "Choose one");
/* SET UP WINDOW FOR THE MENU'S USER INTERFACE */
wUI = derwin(wBorder, 16-2, 40-2, 2, 2);
/* ASSOCIATE THESE WINDOWS WITH THE MENU */
set_menu_win(my_menu, wBorder);
set_menu_sub(my_menu, wUI);
/* MATCH MENU'S COLORS TO THAT OF ITS WINDOWS */
set_menu_fore(my_menu, COLOR_PAIR(REDONWHITE));
set_menu_back(my_menu, COLOR_PAIR(WHITEONRED) | WA_BOLD);
/* SET UP AN ENVIRONMENT CONDUCIVE TO MENUING */
keypad(wUI, TRUE); /* enable detection of function keys */
noecho(); /* user keystrokes don't echo */
curs_set(0); /* make cursor invisible */
/* DISPLAY THE MENU */
post_menu(my_menu);
touchwin(wBorder);
wrefresh(wBorder);
while(my_choice == -1)
{
touchwin(wBorder);
wrefresh(wBorder);
touchwin(wUI); /* refresh prior to getch() */
wrefresh(wUI); /* refresh prior to getch() */
c = getch();
switch(c)
{
case KEY_DOWN:
menu_driver(my_menu, REQ_DOWN_ITEM);
break;
case KEY_UP:
menu_driver(my_menu, REQ_UP_ITEM);
break;
case 10: /* Enter */
my_choice = item_index(current_item(my_menu));
pos_menu_cursor(my_menu);
break;
}
}
unpost_menu(my_menu);
for(ssChoice = 0; ssChoice < n_choices; ++ssChoice)
free_item(my_items[ssChoice]);
free_menu(my_menu);
delwin(wUI);
delwin(wBorder);
touchwin(stdscr);
wrefresh(stdscr);
endwin();
}
I'm having trouble displaying an image (PNG extracted with libpng) into an XCB window, it is always entirely empty/white. I'm pretty sure the PNG extraction is correct since I can perfectly re-write it into another file.
I've tried everything I found (explanations, guides, documentation) and I'm running out of ideas:
Creating an xcb_pixmap_t calling xcb_create_pixmap_from_bitmap_data() with the data taken from the PNG, then calling xcb_copy_area() into the EXPOSE part of the event loop.
Creating an xcb_image_t* calling xcb_image_create_from_bitmap_data() then trying to map it to the window with xcb_image_put(). I've even tried to display each pixel with xcb_image_put_pixel(), but without success.
Code sample:
xcb_pixmap_t pixmap = xcb_create_pixmap_from_bitmap_data(
connection, // xcb_connect(0, 0) (type: xcb_connection_t*)
window, // xcb_generate_id(connection) (type: xcb_window_t)
img.getData(), // uint8_t*
img.getWidth(), // 128
img.getHeight(), // 128
img.getBitDepth(), // 8
screen->black_pixel, // screen = xcb_setup_roots_iterator(xcb_get_setup(connection)).data (type: xcb_screen_t*)
screen->white_pixel,
nullptr);
// "img" is an instance of my own custom class, result of PNG reading
xcb_image_t* image = xcb_image_create_from_bitmap_data(
img.getData(),
img.getWidth(),
img.getHeight()); // image->data seems fine
xcb_image_put(connection,
window,
graphicsContext,
image, 0, 0, 0); // This does nothing
for (unsigned int i = 0; i < screen->height_in_pixels; ++i)
for (unsigned int j = 0; j < screen->width_in_pixels; ++j)
xcb_image_put_pixel(image, j, i, 0); // Displays nothing
[...]
// Into event loop
case XCB_EXPOSE: {
xcb_expose_event_t* exposeEvent = reinterpret_cast<xcb_expose_event_t*>(event);
xcb_copy_area(connection,
pixmap,
window,
graphicsContext,
exposeEvent->x, exposeEvent->y, // Top left x & y coordinates of the source's region to copy
exposeEvent->x, exposeEvent->y, // Top left x & y coordinates of the destination's region to copy to
exposeEvent->width,
exposeEvent->height);
xcb_flush(connection);
break;
}
From the examples I found I saw that it didn't need a colormap, but could that be the case? Could anyone tell me where I've gone wrong?
I threw together a simple xcb image viewer about 4 years ago, but just noticed this question, so apologies for the necromancy.
It uses xcb_image, stb_image and nanosvg, but compiles to a relatively small static binary (with a musl or uclibc toolchain)
#include <xcb/xcb.h>
#include <xcb/xcb_image.h>
#define STBI_NO_HDR
#define STBI_NO_LINEAR
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
#define NANOSVG_IMPLEMENTATION
#include "nanosvg.h"
#define NANOSVGRAST_IMPLEMENTATION
#include "nanosvgrast.h"
int main(int argc, char **argv){
xcb_connection_t *c = xcb_connect(0, 0);
xcb_screen_t *s = xcb_setup_roots_iterator(xcb_get_setup(c)).data;
int w, h, n,
depth = s->root_depth,
win_class = XCB_WINDOW_CLASS_INPUT_OUTPUT,
format = XCB_IMAGE_FORMAT_Z_PIXMAP;
xcb_colormap_t colormap = s->default_colormap;
xcb_drawable_t win = xcb_generate_id(c);
xcb_gcontext_t gc = xcb_generate_id(c);
xcb_pixmap_t pixmap = xcb_generate_id(c);
xcb_generic_event_t *ev;
xcb_image_t *image;
NSVGimage *shapes = NULL;
NSVGrasterizer *rast = NULL;
char *data = NULL;
unsigned *dp;
size_t i, len;
uint32_t mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK,
value_mask = XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_BUTTON_PRESS,
values[] = { s->black_pixel, value_mask };
if (argc<2) return -1;
if ((data = stbi_load(argv[1], &w, &h, &n, 4)))
;
else if ((shapes = nsvgParseFromFile(argv[1], "px", 96.0f))) {
w = (int)shapes->width;
h = (int)shapes->height;
rast = nsvgCreateRasterizer();
data = malloc(w*h*4);
nsvgRasterize(rast, shapes, 0,0,1, data, w, h, w*4);
}else return -1;
for(i=0,len=w*h,dp=(unsigned *)data;i<len;i++) //rgba to bgra
dp[i]=dp[i]&0xff00ff00|((dp[i]>>16)&0xFF)|((dp[i]<<16)&0xFF0000);
xcb_create_window(c,depth,win,s->root,0,0,w,h,1,win_class,s->root_visual,mask,values);
xcb_create_pixmap(c,depth,pixmap,win,w,h);
xcb_create_gc(c,gc,pixmap,0,NULL);
image = xcb_image_create_native(c,w,h,format,depth,data,w*h*4,data);
xcb_image_put(c, pixmap, gc, image, 0, 0, 0);
xcb_image_destroy(image);
xcb_map_window(c, win);
xcb_flush(c);
while ((ev = xcb_wait_for_event(c))) {
switch (ev->response_type & ~0x80){
case XCB_EXPOSE: {
xcb_expose_event_t *x = (xcb_expose_event_t *)ev;
xcb_copy_area(c,pixmap,win,gc,x->x,x->y,x->x,x->y,x->width,x->height);
xcb_flush(c);
}break;
case XCB_BUTTON_PRESS: goto end;
default: break;
}
}
end:
xcb_free_pixmap(c, pixmap);
xcb_disconnect(c);
return 0;
}
Smoke provides an introspective C++ API wrapper to the Qt framework.
https://techbase.kde.org/Development/Languages/Smoke
https://techbase.kde.org/Development/Languages/Smoke/API_Documentation
SmokeC wraps the the Smoke wrapper with C functions to facilitate using the Smoke API from C.
https://github.com/pankajp/pysmoke/blob/master/smokec/smokec.cpp
Working example application in C (functionally identical to the C++ example in the first link above):
https://github.com/pankajp/pysmoke/blob/master/examples/hellowidget.c
I'm trying to modify this example application and add further calls to QWidget object methods, but I can't figure out how to call a method that takes a QString argument, such as setWindowTitle, whose signature is
void setWindowTitle(const QString &);
Here's my best guess:
/* Set the window title... Segfaults */
methId = Smoke_findMethod(classId.smoke, "QWidget", "setWindowTitle$");
klass = Smoke_classes(classId.smoke)[classId.index];
meth = Smoke_methods(methId.smoke)[Smoke_methodMaps(methId.smoke)[methId.index].method];
stack[1].s_voidp = (void*)"hello universe";
(*klass.classFn)(meth.method, widget, stack);
... but this segfaults. Can anyone advise how I should construct a QString argument using the SmokeC wrapper?
My complete modified example application follows.
/*
* File: hellowidget.c
* Author: pankaj
*
* Created on November 19, 2013, 2:41 PM
*/
#include <stdio.h>
#include <string.h>
#include "smokec.h"
#include "bindings.h"
/*
* In a bindings runtime, this should return the classname as used
* in the bindings language, e.g. Qt::Widget in Ruby or
* Qyoto.QWidget in C# or QtGui.QWidget in python
*/
char *className(CSmokeBinding binding, Index classId) {
return (char*) Smoke_classes(CSmoke_FromBinding(binding))[classId].className;
}
void deleted(CSmokeBinding binding, Index classId, void *obj)
{
}
cbool callMethod(CSmokeBinding binding, Index method, void *obj, Stack args, cbool isAbstract)
{
return 0;
}
int main(int argc, char **argv)
{
/* Initialize the Qt SMOKE runtime. */
init_qtcore_CSmoke();
init_qtgui_CSmoke();
CSmoke qtcore_smoke = qtcore_CSmoke();
CSmoke qtgui_smoke = qtgui_CSmoke();
/* Create a SmokeBinding for the Qt SMOKE runtime. */
CSmokeBinding qtcoreBinding = SmokeBinding_new(qtcore_smoke, deleted, callMethod, className);
CSmokeBinding qtguiBinding = SmokeBinding_new(qtgui_smoke, deleted, callMethod, className);
/* Find the 'QApplication' class. */
CModuleIndex classId = findClass("QApplication");
/* find the methodId. we use a munged method signature, where
* $ is a plain scalar
* # is an object
* ? is a non-scalar (reference to array or hash, undef) */
CModuleIndex methId = Smoke_findMethod(classId.smoke, "QApplication", "QApplication$?"); // find the constructor
/* Get the Smoke::Class */
Class klass = Smoke_classes(classId.smoke)[classId.index];
// findMethod() returns an index into methodMaps, which has
// information about the classId, methodNameId and methodId. we
// are interested in the methodId to get a Smoke::Method
Method meth = Smoke_methods(methId.smoke)[Smoke_methodMaps(methId.smoke)[methId.index].method];
StackItem stack[3];
// QApplication expects a reference to argc, so we pass it as a pointer
stack[1].s_voidp = &argc;
stack[2].s_voidp = argv;
// call the constructor, Smoke::Method::method is the methodId
// specifically for this class.
(*klass.classFn)(meth.method, 0, stack);
// the zeroth element contains the return value, in this case the
// QApplication instance
void *qapp = stack[0].s_voidp;
// method index 0 is always "set smoke binding" - needed for
// virtual method callbacks etc.
stack[1].s_voidp = qtguiBinding.binding;
(*klass.classFn)(0, qapp, stack);
// create a widget
classId = findClass("QWidget");
methId = Smoke_findMethod(classId.smoke, "QWidget", "QWidget");
klass = Smoke_classes(classId.smoke)[classId.index];
meth = Smoke_methods(methId.smoke)[Smoke_methodMaps(methId.smoke)[methId.index].method];
(*klass.classFn)(meth.method, 0, stack);
void *widget = stack[0].s_voidp;
// set the smoke binding
stack[1].s_voidp = qtguiBinding.binding;
(*klass.classFn)(0, widget, stack);
/* Show the widget maximized.*/
methId = Smoke_findMethod(classId.smoke, "QWidget", "showMaximized");
meth = Smoke_methods(methId.smoke)[Smoke_methodMaps(methId.smoke)[methId.index].method];
(*klass.classFn)(meth.method, widget, 0);
/* Raise the window to the foreground */
methId = Smoke_findMethod(classId.smoke, "QWidget", "raise");
meth = Smoke_methods(methId.smoke)[Smoke_methodMaps(methId.smoke)[methId.index].method];
(*klass.classFn)(meth.method, widget, 0);
/* Set the modified indicator. */
methId = Smoke_findMethod(classId.smoke, "QWidget", "setWindowModified$");
meth = Smoke_methods(methId.smoke)[Smoke_methodMaps(methId.smoke)[methId.index].method];
stack[1].s_bool = 1;
(*klass.classFn)(meth.method, widget, stack);
/* Set the window title... Segfaults */
methId = Smoke_findMethod(classId.smoke, "QWidget", "setWindowTitle$");
klass = Smoke_classes(classId.smoke)[classId.index];
meth = Smoke_methods(methId.smoke)[Smoke_methodMaps(methId.smoke)[methId.index].method];
stack[1].s_voidp = (void*)"hello universe";
(*klass.classFn)(meth.method, widget, stack);
// we don't even need findClass() when we use the classId provided
// by the MethodMap
methId = Smoke_findMethod(qtgui_smoke, "QApplication", "exec");
klass = Smoke_classes(methId.smoke)[Smoke_methodMaps(methId.smoke)[methId.index].classId];
meth = Smoke_methods(methId.smoke)[Smoke_methodMaps(methId.smoke)[methId.index].method];
// call QApplication::exec()
(*klass.classFn)(meth.method, 0, stack);
// store the return value of QApplication::exec()
int retval = stack[0].s_int;
// destroy the QApplication instance
methId = Smoke_findMethod(qtgui_smoke, "QApplication", "~QApplication");
meth = Smoke_methods(methId.smoke)[Smoke_methodMaps(methId.smoke)[methId.index].method];
(*klass.classFn)(meth.method, qapp, 0);
// destroy the smoke instance
CSmoke_delete(qtgui_smoke);
CSmoke_delete(qtcore_smoke);
// return the previously stored value
return retval;
}
/* Sequential Mandelbrot program mandelbrot.c */
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xos.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#define X_RESN 800 /* x resolution */
#define Y_RESN 800 /* y resolution */
typedef struct complextype
{ float real, imag;
} Compl;
int main (int argc, char *argv[])
{ Window win; /* window initialization */
unsigned
int width, height, /* window size */
x, y, /* window position */
border_width, /* border width in pixels */
display_width,
display_height, /* size of screen */
screen; /* which screen */
char *window_name = "Mandelbrot Set", *display_name = NULL;
GC gc;
unsigned long valuemask = 0;
XGCValues values;
Display *display;
XSizeHints size_hints;
Pixmap bitmap;
XPoint points[800];
FILE *fp, *fopen ();
char str[100];
XSetWindowAttributes attr[1];
/* Mandlebrot variables */
int i, j, k;
Compl z, c;
float lengthsq, temp;
/* connect to Xserver */
if ( (display = XOpenDisplay (display_name)) == NULL )
{ fprintf (stderr, "drawon: cannot connect to X server %s\n",
XDisplayName (display_name) );
exit (-1);
}
/* get screen size */
screen = DefaultScreen (display);
display_width = DisplayWidth (display, screen);
display_height = DisplayHeight (display, screen);
/* set window size */
width = X_RESN;
height = Y_RESN;
/* set window position */
x = 0;
y = 0;
/* create opaque window */
border_width = 4;
win = XCreateSimpleWindow(display, RootWindow (display, screen),
x, y, width, height, border_width,
BlackPixel (display, screen), WhitePixel (display, screen));
size_hints.flags = USPosition|USSize;
size_hints.x = x;
size_hints.y = y;
size_hints.width = width;
size_hints.height = height;
size_hints.min_width = 300;
size_hints.min_height = 300;
XSetNormalHints (display, win, &size_hints);
XStoreName(display, win, window_name);
/* create graphics context */
gc = XCreateGC (display, win, valuemask, &values);
XSetBackground (display, gc, WhitePixel (display, screen));
XSetForeground (display, gc, BlackPixel (display, screen));
XSetLineAttributes (display,gc,1,LineSolid,CapRound,JoinRound);
attr[0].backing_store = Always;
attr[0].backing_planes = 1;
attr[0].backing_pixel = BlackPixel(display, screen);
XChangeWindowAttributes(display, win,
CWBackingStore | CWBackingPlanes | CWBackingPixel, attr);
XMapWindow (display, win);
XSync(display, 0);
/* Calculate and draw points */
for(i=0; i < X_RESN; i++)
{ for(j=0; j < Y_RESN; j++)
{ z.real = z.imag = 0.0; /* 800x800 scale factors */
c.real = ((float) j - 400.0)/200.0;
c.imag = ((float) i - 400.0)/200.0;
k = 0;
do
{ /* iterate for pixel color */
temp = z.real*z.real - z.imag*z.imag + c.real;
z.imag = 2.0*z.real*z.imag + c.imag;
z.real = temp;
lengthsq = z.real*z.real+z.imag*z.imag;
k++;
} while (lengthsq < 4.0 && k < 100);
if (k == 100) XDrawPoint (display, win, gc, j, i);
} }
XFlush (display);
sleep (30);
/* Program Finished */
}
Open up a command prompt and run the two commands:
find / -type f - name Xlib.h
find / -type d - name X11
That should help you locate that file and/or directory so you can see whether it's available.
If it is available (e.g., it's found at /usr/include/X_stuff/X11/Xlib.h), make sure your compiler command references it, like:
g++ -I/usr/include/X_stuff ...
If it's not there, install it.
Either you forgot to install the X development packages, or the directory containing the headers was not added to the includedir list.