I have written the attached example program that uses GTK2 in conjunction with EGL and OpenGL. On my system this works fine. On the PC of friend it only will produce a black window and I cannot put my finger on why this happens. We event straces which libraries get loaded (Which are the same). My PC has a NVIDIA MX150, he has a GTX 1030, he uses Debian Strech and I use Debian buster.
I cannot put my finger on the problem. Anyways, heres the code:
#include <stdio.h>
#include <gtk/gtk.h>
#include <gdk/gdkx.h>
#include <EGL/egl.h>
#include <GL/gl.h>
static EGLDisplay egl_display;
static EGLSurface egl_surface;
static EGLContext egl_context;
static void realize_cb (GtkWidget *widget)
{
printf("REALIZE\n");
EGLConfig egl_config;
EGLint n_config;
EGLint attributes[] = { EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT,
EGL_NONE };
EGLint surf_attrs[] = {
EGL_RENDER_BUFFER, EGL_BACK_BUFFER,
EGL_NONE
};
egl_display = eglGetDisplay ((EGLNativeDisplayType) gdk_x11_display_get_xdisplay (gtk_widget_get_display (widget)));
eglInitialize (egl_display, NULL, NULL);
eglChooseConfig (egl_display, attributes, &egl_config, 1, &n_config);
eglBindAPI (EGL_OPENGL_API);
egl_surface = eglCreateWindowSurface (egl_display, egl_config, GDK_WINDOW_XID (gtk_widget_get_window (widget)), surf_attrs);
egl_context = eglCreateContext (egl_display, egl_config, EGL_NO_CONTEXT, NULL);
}
static gboolean on_configure (GtkWidget *widget, GdkEvent *event, gpointer user_data)
{
gtk_widget_queue_draw(widget);
return FALSE;
}
static gboolean draw_cb (GtkWidget *widget, GdkEventExpose *expose, gpointer userdata)
{
printf("DRAW\n");
eglMakeCurrent (egl_display, egl_surface, egl_surface, egl_context);
GtkAllocation alloc;
gtk_widget_get_allocation(widget, &alloc);
glViewport (0, 0, alloc.width, alloc.height);
glClearColor (0, 0, 0, 1);
glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glMatrixMode (GL_PROJECTION);
glLoadIdentity ();
glOrtho (0, 100, 0, 100, 0, 1);
glBegin (GL_TRIANGLES);
glColor3f (1, 0, 0);
glVertex2f (50, 10);
glColor3f (0, 1, 0);
glVertex2f (90, 90);
glColor3f (0, 0, 1);
glVertex2f (10, 90);
glEnd ();
eglSwapBuffers (egl_display, egl_surface);
return TRUE;
}
int main (int argc, char **argv)
{
GtkWidget *w;
gtk_init (&argc, &argv);
w = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_widget_set_double_buffered (GTK_WIDGET (w), FALSE);
g_signal_connect (G_OBJECT (w), "realize", G_CALLBACK (realize_cb), NULL);
g_signal_connect (G_OBJECT (w), "expose-event", G_CALLBACK (draw_cb), NULL);
g_signal_connect (G_OBJECT (w), "configure-event", G_CALLBACK (on_configure), NULL);
gtk_widget_show (w);
gtk_main ();
return 0;
}
Related
This is my code. I hope it can draw a circle when users press button. The circel is centered around the point where the mouse key pressed. But the program doesn't run as expected. It clears the pre-circles and draws a new circle. Are there some functions to solve this problem?
My codes:
#include <gtk/gtk.h>
static void do_drawing(cairo_t *);
struct {
int count;
double coordx[100];
double coordy[100];
} glob;
static gboolean on_draw_event(GtkWidget *widget, cairo_t *cr,
gpointer user_data)
{
do_drawing(cr);
return FALSE;
}
static void do_drawing(cairo_t *cr)
{
cairo_set_source_rgb(cr, 0, 0, 0);
cairo_set_line_width(cr, 0.5);
if(glob.count)
{
cairo_set_font_size(cr, 20);
cairo_new_sub_path(cr);
cairo_arc(cr, glob.coordx[glob.count-1], glob.coordy[glob.count-1], 20, 0, 2*G_PI);
cairo_move_to(cr, glob.coordx[glob.count-1]-3, glob.coordy[glob.count-1] + 7);
cairo_show_text(cr, "i");
cairo_stroke(cr);
}
}
static gboolean clicked(GtkWidget *widget, GdkEventButton *event,
gpointer user_data)
{
if (event->button == 1) {
glob.coordx[glob.count] = event->x;
glob.coordy[glob.count++] = event->y;
gtk_widget_queue_draw(widget);
}
return TRUE;
}
int main(int argc, char *argv[])
{
GtkWidget *window;
GtkWidget *darea;
glob.count = 0;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
darea = gtk_drawing_area_new();
gtk_container_add(GTK_CONTAINER(window), darea);
gtk_widget_add_events(darea, GDK_BUTTON_PRESS_MASK);
g_signal_connect(G_OBJECT(darea), "draw",
G_CALLBACK(on_draw_event), NULL);
g_signal_connect(window, "destroy",
G_CALLBACK(gtk_main_quit), NULL);
g_signal_connect(darea, "button-press-event",
G_CALLBACK(clicked), NULL);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_default_size(GTK_WINDOW(window), 400, 300);
gtk_window_set_title(GTK_WINDOW(window), "Lines");
gtk_widget_show_all(window);
gtk_main();
return 0;
}
I know the gtk_widget_queue_draw(widget) will redraw the GtkDrawingArea entirely, So which function can replace it? I have another question: If I wan to draw on GtkDrawingArea, should I create cairo_t before drawing? How to create a cairo_t for GtkDrawingArea?
I'm writing an application in Linux using Xlib to manage a window and cairo to draw some text in it. The text content of the window changes during the execution, so I want to adapt the window size to match that of the text extent. If the size of the text extent does not change, the window is always correctly updated with the new text.
But when the text extent changes, and so the window is resized accordingly, the window is cleared but the new text is never shown. Only if there is no call to XResizeWindow the text is actually displayed. The code I'm using is
if (/* Text extent is changed */)
{
XResizeWindow (display, window, new_width, new_height);
cairo_xlib_surface_set_size (surface, new_width, new_height);
}
XClearWindow (display, window);
/* ... Cairo code to draw the text ... */
// cairo_surface_flush (surface);
// XFlush (display);
I have also tried to add after the Cairo code that draws the text the methods cairo_surface_flush and XFlush (commented in the example) but nothing changes.
EDIT: I solved the problem using two threads: the first thread with the usual loop for listening to the Expose events plus the code to redraw the content and the second thread that issues the resize of the window and sends an Expose event to wake up the first thread.
In this example the window is resized every 500 ms to random width and height and a progressive counter is displayed in it at every resize. I use C++11, compile with:
g++ -std=c++11 -o test test.cpp -lX11 -lcairo -lpthread
The code is:
#include <random>
#include <chrono>
#include <thread>
#include <string>
#include <X11/Xlib.h>
#include <cairo/cairo-xlib.h>
Display * d;
Window w;
cairo_surface_t * surface;
int width = 300, height = 300;
unsigned char counter = 0;
std::random_device rd;
std::knuth_b gen (rd ());
std::uniform_int_distribution < > dist (150, 300);
void logic ()
{
XEvent send_event;
send_event.type = Expose;
send_event.xexpose.window = w;
while (true)
{
std::this_thread::sleep_for (std::chrono::milliseconds (500));
++ counter;
width = dist (gen);
height = dist (gen);
cairo_xlib_surface_set_size (surface, width, height);
XResizeWindow (d, w, width, height);
XSendEvent (d, w, False, ExposureMask, & send_event);
XFlush (d);
}
}
int main ( )
{
XInitThreads ();
d = XOpenDisplay (NULL);
w = XCreateSimpleWindow (d, RootWindow (d, 0), 0, 0, width, height, 0, 0, 0x000000);
XMapWindow (d, w);
XSelectInput (d, w, ExposureMask | KeyPressMask);
surface = cairo_xlib_surface_create (d, w, DefaultVisual (d, 0), width, height);
cairo_t * cairo = cairo_create (surface);
cairo_select_font_face (cairo, "FreeSans", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD);
cairo_set_font_size (cairo, 40 );
cairo_set_source_rgb (cairo, 0.8, 0.8, 0.8);
cairo_move_to (cairo, 40.0, 60.0);
cairo_show_text (cairo, std::to_string (counter).c_str ());
XFlush (d);
std::thread T (logic);
XEvent event;
while (true)
{
XNextEvent (d, & event);
if (event.type == Expose)
{
XClearWindow (d, w);
cairo_move_to (cairo, 40.0, 60.0);
cairo_show_text (cairo, std::to_string (counter).c_str ());
}
else if (event.type == KeyPress)
{
XCloseDisplay (d);
return 0;
}
}
}
But a question remains: is it possible to obtain the same result using only one thread?
Here is a single-threaded version of your code. It is not nice, but it seems to work. The hard part is waiting for events from the X11 server and the timeout simultaneously. I do this with select() in the following code.
Note that I also handle ConfigureNotify events instead of assuming that XResizeWindow always does just what we want.
#include <random>
#include <chrono>
#include <thread>
#include <string>
#include <X11/Xlib.h>
#include <cairo/cairo-xlib.h>
#include <sys/time.h>
Display * d;
Window w;
cairo_surface_t * surface;
int width = 300, height = 300;
unsigned char counter = 0;
std::random_device rd;
std::knuth_b gen (rd ());
std::uniform_int_distribution < > dist (150, 300);
void do_update ()
{
++ counter;
width = dist (gen);
height = dist (gen);
XResizeWindow (d, w, width, height);
// Force a redraw
XClearArea(d, w, 0, 0, 0, 0, True);
}
int main ( )
{
XInitThreads ();
d = XOpenDisplay (NULL);
w = XCreateSimpleWindow (d, RootWindow (d, 0), 0, 0, width, height, 0, 0, 0x000000);
XMapWindow (d, w);
XSelectInput (d, w, ExposureMask | KeyPressMask);
surface = cairo_xlib_surface_create (d, w, DefaultVisual (d, 0), width, height);
cairo_t * cairo = cairo_create (surface);
cairo_select_font_face (cairo, "FreeSans", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD);
cairo_set_font_size (cairo, 40 );
cairo_set_source_rgb (cairo, 0.8, 0.8, 0.8);
cairo_move_to (cairo, 40.0, 60.0);
cairo_show_text (cairo, std::to_string (counter).c_str ());
XFlush (d);
struct timeval next_update;
struct timeval now;
struct timeval interval = { 0, 500000 };
gettimeofday(&now, NULL);
timeradd(&now, &interval, &next_update);
while (true)
{
XEvent event;
gettimeofday(&now, NULL);
if (timercmp(&now, &next_update, >)) {
// Store time of next update
timeradd(&now, &interval, &next_update);
puts("update");
do_update();
}
if (!XPending(d)) {
struct timeval remaining;
fd_set fds;
int fd = ConnectionNumber(d);
FD_ZERO(&fds);
FD_SET(fd, &fds);
timersub(&next_update, &now, &remaining);
select(fd + 1, &fds, NULL, NULL, &remaining);
} else {
XNextEvent (d, & event);
if (event.type == Expose)
{
XClearWindow (d, w);
cairo_move_to (cairo, 40.0, 60.0);
cairo_show_text (cairo, std::to_string (counter).c_str ());
}
if (event.type == ConfigureNotify)
{
cairo_xlib_surface_set_size (surface, event.xconfigure.width, event.xconfigure.height);
}
else if (event.type == KeyPress)
{
XCloseDisplay (d);
return 0;
}
}
}
}
I am going to create a simple user interface that has a button which controls a value, and when user click on it, a vscale appear in new window or dialog or box and so on to set value with scale.
is there any example of such program?
I am googled and find some examples but they set color and the dialog have functions for font selection and... .
Win 7_64 bit, gtk3.4.2, Visual c++
regards
Here is the code :
#include <gtk/gtk.h>
void value_changed(GtkRange *range, gpointer win) {
gdouble val = gtk_range_get_value(range);
gchar *str = g_strdup_printf("%.f", val);
gtk_label_set_text(GTK_LABEL(win), str);
g_free(str);
}
void value(GtkRange *range, gpointer win)
{
GtkWidget *halign;
GtkWidget *hbox;
GtkWidget *hscale;
GtkWidget *label;
GtkWidget *content_area, *label1;
GtkWidget * dia = gtk_dialog_new();
content_area = gtk_dialog_get_content_area (GTK_DIALOG (dia));
hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL,FALSE);
hscale = gtk_scale_new_with_range(GTK_ORIENTATION_HORIZONTAL,0, 100, 1);
gtk_scale_set_draw_value(GTK_SCALE(hscale), FALSE);
gtk_widget_set_size_request(hscale, 150, -1);
label = gtk_label_new("...");
gtk_misc_set_alignment(GTK_MISC(label), 0.0, 1);
gtk_box_pack_start(GTK_BOX(hbox), hscale, FALSE, FALSE, 0);
gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
halign = gtk_alignment_new(0, 0, 0, 0);
gtk_container_add(GTK_CONTAINER(halign), hbox);
gtk_container_add(GTK_CONTAINER(content_area), halign);
g_signal_connect(hscale, "value-changed",
G_CALLBACK(value_changed), label);
label1 = gtk_label_new ("YA ali");
gtk_container_add (GTK_CONTAINER (content_area), label1);
gtk_widget_show_all (dia);
}
int main(int argc, char *argv[]) {
GtkWidget *window;
GtkWidget *halign;
GtkWidget *hbox;
GtkWidget *hscale;
GtkWidget *label;
GtkWidget *table;
GtkWidget *btn;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_default_size(GTK_WINDOW(window), 300, 250);
gtk_container_set_border_width(GTK_CONTAINER(window), 10);
gtk_window_set_title(GTK_WINDOW(window), "GtkHScale");
btn = gtk_button_new();
table = gtk_table_new(10,10,TRUE);
gtk_table_set_row_spacings(GTK_TABLE(table), 10);
gtk_table_set_col_spacings(GTK_TABLE(table), 10);
gtk_table_attach_defaults(GTK_TABLE(table),btn,2,4,4,6);
gtk_container_add(GTK_CONTAINER(window), table);
g_signal_connect(G_OBJECT(btn), "clicked",
G_CALLBACK(value), G_OBJECT(window));
g_signal_connect(window, "destroy",
G_CALLBACK(gtk_main_quit), NULL);
gtk_widget_show_all(window);
gtk_main();
return 0;
}
#include "stdafx.h"
#include <gl/glut.h>
void resizeEvent(int w, int h)
{
glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0, w, h, 0);
glMatrixMode(GL_MODELVIEW);
glutPostRedisplay();
}
void displayEvent()
{
glClearColor(1, 1, 1, 0);
glClear(GL_COLOR_BUFFER_BIT);
glutSwapBuffers();
char text[] = "Hello World!";
glRasterPos2d(110, 110);
glColor3f(1, 0, 0);
for(int i=0; text[i] != '\0'; i++)
{
glutBitmapCharacter(GLUT_BITMAP_8_BY_13, text[i]);
}
}
int main(int argc, char *argv[])
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
glutInitWindowSize(400, 300);
glutInitWindowPosition(0, 0);
glutCreateWindow("Hello");
glutDisplayFunc(displayEvent);
glutReshapeFunc(resizeEvent);
glutMainLoop();
return 0;
}
I learn from a ppt,and think it will print a string.But it does not work .I search from google,but no answer. I don't know why it can't print a string.
if glutBitmapCharacter is wrong ?
All the commands in opengl, renders on the frame buffer only when flush or finish call happens, so all your commands are still in queue and yet to be displayed on frame buffer. You need to call glFlush/glFinish at the end, glut provides glutSwapBuffers, so in the end of your displayEvent function you need to call glutSwapBuffers to see the effect of your displayEvent function.
How can I change the fontcolor of the statusbar?
I have no Idea.
I found gtk_widget_modify_textbut don't know how to use it!
EDIT:
I tried this, but doesnt worked:
GdkRGBA font_color;
font_color.red = 1;
font_color.green = 0;
font_color.blue = 0;
font_color.alpha = 1;
gtk_widget_override_color(statusbar, GTK_STATE_FLAG_NORMAL, &font_color);
You can use the gtk_widget_override_color and use it with GTK_STATE_FLAG_NORMAL and the corresponding GdkRGBA color you wish to set. See the reference for further details.
Here is an example written in vala:
using Gtk;
public class Application : Gtk.Window {
public Application () {
this.destroy.connect (Gtk.main_quit);
this.set_default_size (100, 50);
Gtk.Statusbar bar = new Gtk.Statusbar ();
this.add(bar);
uint context_id = bar.get_context_id ("example");
bar.push (context_id, "Message ...");
Gdk.RGBA font_color = Gdk.RGBA ();
font_color.red=0.5;
font_color.green=0;
font_color.blue=0;
font_color.alpha=1;
bar.override_color (Gtk.StateFlags.NORMAL, font_color);
}
public static int main (string[] args) {
Gtk.init (ref args);
Application app = new Application ();
app.show_all ();
Gtk.main ();
return 0;
}
}
Here is a C example. It is a modified version of the statusbar example.
#include <stdlib.h>
#include <gtk/gtk.h>
#include <glib.h>
GtkWidget *status_bar;
static void push_item( GtkWidget *widget,
gpointer data )
{
static int count = 1;
gchar *buff;
buff = g_strdup_printf ("Item %d", count++);
gtk_statusbar_push (GTK_STATUSBAR (status_bar), GPOINTER_TO_INT (data), buff);
g_free (buff);
}
int main( int argc,
char *argv[] )
{
GtkWidget *window;
GtkWidget *vbox;
GtkWidget *button;
gint context_id;
gtk_init (&argc, &argv);
/* create a new window */
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_widget_set_size_request (GTK_WIDGET (window), 200, 100);
gtk_window_set_title (GTK_WINDOW (window), "GTK Statusbar Example");
g_signal_connect (window, "delete-event",
G_CALLBACK (exit), NULL);
vbox = gtk_vbox_new (FALSE, 1);
gtk_container_add (GTK_CONTAINER (window), vbox);
gtk_widget_show (vbox);
status_bar = gtk_statusbar_new ();
gtk_box_pack_start (GTK_BOX (vbox), status_bar, TRUE, TRUE, 0);
gtk_widget_show (status_bar);
/* here comes the color change */
GdkRGBA font_color;
font_color.red = 1;
font_color.green = 0;
font_color.blue = 0;
font_color.alpha = 1;
gtk_widget_override_color(status_bar, GTK_STATE_FLAG_NORMAL, &font_color);
context_id = gtk_statusbar_get_context_id(
GTK_STATUSBAR (status_bar), "Statusbar example");
button = gtk_button_new_with_label ("push item");
g_signal_connect (button, "clicked",
G_CALLBACK (push_item), GINT_TO_POINTER (context_id));
gtk_box_pack_start (GTK_BOX (vbox), button, TRUE, TRUE, 2);
gtk_widget_show (button);
/* always display the window as the last step so it all splashes on
* the screen at once. */
gtk_widget_show (window);
gtk_main ();
return 0;
}
I think you need to retrieve the "label" widget of the status bar to change its attributes. This code changes font and size of the status bar (GTK 2):
PangoFontDescription *pfd = pango_font_description_from_string("Consolas 8");
GtkWidget *w = gtk_statusbar_get_message_area(GTK_STATUSBAR(statusbar));
GList *gl = gtk_container_get_children(GTK_CONTAINER(w));
GtkWidget *ch = GTK_WIDGET(gl->data);
GtkLabel *label = GTK_LABEL(ch);
printf("Number of children: %d Text: %s\n", g_list_length(gl), gtk_label_get_text(label));
//prints 1 and the current message of the statusbar
gtk_widget_modify_font(ch, pfd);
g_list_free(gl);
pango_font_description_free(pfd);