GTK window motion animation? - linux

I want to move my GTK_WINDOW across the screen automatically. Currently I have it in a draw/move loop, but that's terribly choppy. I'm very new to GTK programming (and gui programming in general). What am I missing?

You haven't said what sort of path you want the window to follow. If the path is some simple function of time -- that is, if you have a way to compute where you want the window to be at any given time -- you could try the method illustrated in following code. For the quite-simple menu in the example, it works ok on my Linux system and produces fairly smooth motion.
The key to the method is that instead of moving the window a given distance per timer event, it finds out the current time and moves the window to the location it should be at, at that time. Thus, the time derivative of speed of motion should be constant, which avoids ragged or choppy motion even if timer events occur irregularly. (As noted in g-timeout-add() description, irregularity can easily occur.)
In this example, the path is from top left of window to bottom left and back, repeatedly. The constant 'HalfTime' in timerEvent() controls how long it takes to move from corner to corner. The constant 3 in the g_timeout_add() call sets the timer interval to 0.003 seconds, or about 333 moves per second (MPS). (You may want to try more-reasonable rates, such as 20, 30, 40, etc MPS; I used the number 3 because I didn't look up g-timeout-add() before using it, and assumed the delay was hundreths of seconds, for about 33 MPS, rather than milliseconds, for about 333 MPS.) If your window contents are quite complex, fewer MPS will be practical. Also, I tried some slower rates and got more of an impression of choppiness.
/* $Id: app12.c $
Re: animating position of a top-level Gtk window
jiw July 2011 -- Offered without warranty under GPL v3
terms per http://www.gnu.org/licenses/gpl.html */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <gtk/gtk.h>
typedef struct DATA { GTimer *timer; GtkWidget *window; int w, h; }
DataStruct;
gboolean timerEvent(void *dataset) {
enum { HalfTime=8, CycTime=2*HalfTime };
gulong micros;
DataStruct *data =dataset;
double t = fabs(fmod (g_timer_elapsed (data->timer, &micros), CycTime));
int x = (t*data->w)/HalfTime, y = (t*data->h)/HalfTime;
gtk_window_move (GTK_WINDOW(data->window),
t<HalfTime? x : 2*data->w-x, t<HalfTime? y : 2*data->h-y);
return TRUE; /* Keep timeout running */
}
int main(int argc, char **argv) {
GtkWidget *vbox, *b;
GdkScreen *gds;
DataStruct data;
data.timer = g_timer_new();
gtk_init (&argc, &argv);
data.window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_window_set_default_size (GTK_WINDOW(data.window), 200, 150);
g_signal_connect (G_OBJECT(data.window), "destroy",
G_CALLBACK(gtk_main_quit), NULL);
vbox = gtk_vbox_new (FALSE, 0);
gtk_container_add (GTK_CONTAINER(data.window), vbox);
b = gtk_button_new_with_label ("Click to Exit");
gtk_box_pack_start (GTK_BOX(vbox), b, TRUE, TRUE, TRUE);
g_signal_connect (b, "clicked", G_CALLBACK(gtk_main_quit), NULL);
gtk_widget_show_all (data.window);
gds = gdk_screen_get_default (); /* Get pointer to screen */
data.w = gdk_screen_get_width (gds); /* Find out screen width */
data.h = gdk_screen_get_height (gds); /* Find out screen height */
printf ("Screen size = %d by %d\n", data.w, data.h); fflush(stdout);
g_timeout_add(3, timerEvent, &data); /* Create .003 sec timer */
gtk_main();
return (0);
}

Related

Newbie Issue with X11 Double buffering

I botched together the following code to do a simple rotating radar display.
Q1) How can I eliminate the flicker of the line being drawn and then drawn over. Can I use double buffering somehow ?
Q2) How can I get Mouse and Keyborad inputs and process/parse them.
My goal is to read angle and distance data from an ultrasonic sensor and display that as a sweep. This data comes from an Arduino with an ultrasonic rangefinder mounted on a servo sweeping backwards and forwards 180 Deg. I also cant quite get the math right. I'd like it to go from 0 to 180, left to right and back depending on the angle data from the Arduino. 0 Deg is 90 Deg off to the right. So I know I have to offset it by 180 Deg but am hopeless at math. The sweep would have to start at 270 Deg and sweep through to 0 Deg. I will have to learn how to read the serial port and parse the data for the display.
Any help would be appreciated.
Pls don't jump on me. Just trying to learn.
Here is my code. I used a tutorial as a base and kind of guessed my way through.
CODE:
/*
* simple-drawing.c - demonstrate drawing of pixels, lines, arcs, etc.
* on a window. All drawings are done in black color
* over a white background.
*/
#include <X11/Xlib.h>
#include <stdio.h>
#include <stdlib.h> /* getenv(), etc. */
#include <unistd.h> /* sleep(), etc. */
// Include math library functions.
#include <math.h>
/*
* function: create_simple_window. Creates a window with a white background
* in the given size.
* input: display, size of the window (in pixels), and location of the window
* (in pixels).
* output: the window's ID.
* notes: window is created with a black border, 2 pixels wide.
* the window is automatically mapped after its creation.
*/
Window
create_simple_window(Display* display, int width, int height, int x, int y)
{
int screen_num = DefaultScreen(display);
int win_border_width = 2;
Window win;
/* create a simple window, as a direct child of the screen's */
/* root window. Use the screen's black and white colors as */
/* the foreground and background colors of the window, */
/* respectively. Place the new window's top-left corner at */
/* the given 'x,y' coordinates. */
win = XCreateSimpleWindow(display, RootWindow(display, screen_num),
x, y, width, height, win_border_width,
BlackPixel(display, screen_num),
WhitePixel(display, screen_num));
/* make the window actually appear on the screen. */
XMapWindow(display, win);
/* flush all pending requests to the X server. */
XFlush(display);
return win;
}
GC
create_gc(Display* display, Window win, int reverse_video)
{
GC gc; /* handle of newly created GC. */
unsigned long valuemask = 0; /* which values in 'values' to */
/* check when creating the GC. */
XGCValues values; /* initial values for the GC. */
unsigned int line_width = 2; /* line width for the GC. */
int line_style = LineSolid; /* style for lines drawing and */
int cap_style = CapButt; /* style of the line's edje and */
int join_style = JoinBevel; /* joined lines. */
int screen_num = DefaultScreen(display);
gc = XCreateGC(display, win, valuemask, &values);
if (gc < 0) {
fprintf(stderr, "XCreateGC: \n");
}
/* allocate foreground and background colors for this GC. */
if (reverse_video) {
XSetForeground(display, gc, WhitePixel(display, screen_num));
XSetBackground(display, gc, BlackPixel(display, screen_num));
}
else {
XSetForeground(display, gc, BlackPixel(display, screen_num));
XSetBackground(display, gc, WhitePixel(display, screen_num));
}
/* define the style of lines that will be drawn using this GC. */
XSetLineAttributes(display, gc,
line_width, line_style, cap_style, join_style);
/* define the fill style for the GC. to be 'solid filling'. */
XSetFillStyle(display, gc, FillSolid);
return gc;
}
void
main(int argc, char* argv[])
{
Display* display; /* pointer to X Display structure. */
int screen_num; /* number of screen to place the window on. */
Window win; /* pointer to the newly created window. */
unsigned int display_width,
display_height; /* height and width of the X display. */
unsigned int width, height; /* height and width for the new window. */
char *display_name = getenv("DISPLAY"); /* address of the X display. */
GC gc; /* GC (graphics context) used for drawing */
/* in our window. */
// Added code to select simple black or white color to draw.
Colormap screen_colormap; /* color map to use for allocating colors. */
XColor black, white;
/* used for allocation of the given color */
/* map entries. */
Status rc; /* return status of various Xlib functions. */
// Johns stuff. This code draws a line from starting co-ordinates at an angle
//
int angle=0;
int x1,x2,y1,y2;
int px,py;
// End
/* open connection with the X server. */
display = XOpenDisplay(display_name);
if (display == NULL) {
fprintf(stderr, "%s: cannot connect to X server '%s'\n",
argv[0], display_name);
exit(1);
}
/* get the geometry of the default screen for our display. */
screen_num = DefaultScreen(display);
display_width = DisplayWidth(display, screen_num);
display_height = DisplayHeight(display, screen_num);
/* make the new window occupy 1/9 of the screen's size. */
width = (display_width / 2);
height = (display_height / 2);
printf("window width - '%d'; height - '%d'\n", width, height);
/* create a simple window, as a direct child of the screen's */
/* root window. Use the screen's white color as the background */
/* color of the window. Place the new window's top-left corner */
/* at the given 'x,y' coordinates. */
win = create_simple_window(display, width, height, 50, 50);
/* allocate a new GC (graphics context) for drawing in the window. */
gc = create_gc(display, win, 0);
XSync(display, False);
// Following 3 lines needed or else we just see white background.
/* catch expose events */
XSelectInput(display, win, ExposureMask);
/* wait for the expose event */
XEvent ev;
XNextEvent(display, &ev);
// Follwing code borrowed to get colors to draw.
/* get access to the screen's color map. */
screen_colormap = DefaultColormap(display, DefaultScreen(display));
/* allocate the set of colors we will want to use for the drawing. */
rc = XAllocNamedColor(display, screen_colormap, "black", &black, &black);
if (rc == 0) {
fprintf(stderr, "XAllocNamedColor - failed to allocated 'black' color.\n");
exit(1);
}
rc = XAllocNamedColor(display, screen_colormap, "white", &white, &white);
if (rc == 0) {
fprintf(stderr, "XAllocNamedColor - failed to allocated 'white' color.\n");
exit(1);
}
/* draw one pixel near each corner of the window */
XDrawPoint(display, win, gc, 5, 5);
XDrawPoint(display, win, gc, 5, height-5);
XDrawPoint(display, win, gc, width-5, 5);
XDrawPoint(display, win, gc, width-5, height-5);
// Do our display. Loop forever because I don't know how to process keystrokes.
// If we start from the center of the display, 0 is to the right (E), 90 is to the
// bottom (S), 180 (W),270 (N). Don't quite know how to "rotate" the "beam" 180 Deg.
// Needs double buffering to remove the flicker.
// I would like to make a seperate subroutine eg: DrawRadar( int angle, int distance);
x1=width / 2; //Find center of display.
y1=height / 2;
px=x1; // Previous X and Y
py=y1;
while(1){
for(angle=0; angle < 360; angle++)
{
// Distance set to 150 pixels. Radar returns value up to 3300 mm.
// Will have to scale down.
x2=x1 + cos((angle * M_PI) / 180) * 150; // Must convert to radians.
y2=y1 + sin((angle * M_PI) / 180) * 150; // " " " "
XSetForeground(display, gc, black.pixel); //
XDrawLine(display, win, gc, x1, y1, x2, y2);
px=x2;
py=y2;
XFlush(display); // Display what we've drawn.
usleep(3500); // Wait so eye can see it.
XSetForeground(display, gc, white.pixel); // Rub out the line we just drew.
XDrawLine(display, win, gc, x1, y1, px ,py);
XFlush(display); // Causes flickering but only way at the moment.
XSetForeground(display, gc, black.pixel);
usleep(3500);
}
}
// End. Never get past here.
/* flush all pending requests to the X server. */
XFlush(display);
XSync(display, False);
/* make a delay for a short period. */
sleep(4);
/* close the connection to the X server. */
XCloseDisplay(display);
}
Simply trying to draw a moving line without flicker. I understand the concept but am a crap coder. Any help would be appreciated.
Thanks, John.

fltk setting button's active label color

I am using fltk 1.3.2.
I set the button's label color with
_button->labelcolor(fl_rgb_color(162, 60, 62));
but when I press the button, the color is changed.
I couldn't find the function how set the active label color.
Does anyone know how to do that?
Edit:
I use Fl::background() and Fl::foreground() functions before creating the window. This make the problem.
Edit2:
This example shows the problem.
#include <FL/Fl.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Button.H>
#include <iostream>
void HitMe(Fl_Widget* w)
{
std::cout << "Ouch" << std::endl;
}
int main(int argc, char ** argv)
{
Fl::background(0x60, 0x66, 0x60);
Fl_Window *window = new Fl_Window(320,130);
Fl_Button *b = new Fl_Button(10, 10, 130, 30, "A red label");
b->labelcolor(fl_rgb_color(162, 60, 20));
b->callback(HitMe);
window->end();
window->show(argc,argv);
return Fl::run();
}
When I commented out Fl::background() function everything is alright.
What you are seeing is the contrasting colour (see commented out code below). FLTK does this when the button is pressed. It gets the colour of the button, works out the contrasting colour based on the foreground and background colours. Have a look at the help for fl_contrast for more details.
Basically, if there is enough contrast, it will use the foreground colour otherwise it will find a contrasting colour for your background.
What can you do about it?
do nothing - that is how it is
choose a lighter background colour which will satisfy the contrast conditions
make your own button type with its own draw method
class KeepFGButton : public Fl_Button
{
public:
KeepFGButton(int x, int y, int w, int h, const char* s)
: Fl_Button(x, y, w, h, s)
{
}
void draw() {
if (type() == FL_HIDDEN_BUTTON) return;
Fl_Color col = value() ? selection_color() : color();
draw_box(value() ? (down_box() ? down_box() : fl_down(box())) : box(), col);
draw_backdrop();
// Remove the code that changes the contrast
//if (labeltype() == FL_NORMAL_LABEL && value()) {
// Fl_Color c = labelcolor();
// labelcolor(fl_contrast(c, col));
// draw_label();
// labelcolor(c);
//}
//else
draw_label();
if (Fl::focus() == this) draw_focus();
}
};
int main(int argc, char ** argv)
{
Fl::background(0x60, 0x66, 0x60);
Fl_Window *window = new Fl_Window(320, 130);
Fl_Button *b = new KeepFGButton(10, 10, 130, 30, "A red label");
...
Try the following and let me know if, when you run it, the label becomes white. If it doesn't then there is possibly something else that you are doing that is not quite right. If it does, I don't know what the problem is.
#include <FL/Fl.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Button.H>
#include <iostream>
void HitMe(Fl_Widget* w)
{
std::cout << "Ouch" << std::endl;
}
int main(int argc, char ** argv) {
Fl_Window *window = new Fl_Window(320,130);
Fl_Button *b = new Fl_Button(10, 10, 130, 30, "A red label");
b->labelcolor(fl_rgb_color(162, 60, 20));
b->callback(HitMe);
window->end();
window->show(argc,argv);
return Fl::run();
}
This is definitely too late to help the author, but in the event that this helps anyone else searching for an answer to this still relevant issue in FLTK, I'm going to offer my solution:
If you dig through the source code for FLTK 1.3.4-2 (current stable as of this post), there are a couple of built-in colormap indices which are referenced when the drawing of shadow boxes or frames are called: FL_DARK3 (for the boxes) and FL_DARK2 (for the scrollbar back color in Fl_Scroll). You can look at this FLTK documentation to see how to reset these colors to anything you wish at runtime. In particular, anticipating that this ugly red default mess will show up whenever there's a sufficiently dark background, it works well for me to just set these to a slightly lighter version of the overlayed box color:
Fl_Color boxColor = 0x88888800;
Fl_Color boxShadowColor = 0xaaaaaa00;
Fl::set_color(FL_DARK3, boxShadowColor);
Fl::set_color(FL_DARK2, boxShadowColor);
Now create your label as above and the display will be free of the red shadow. N.b. it is also possible to override the standard background2() behavior which resets FL_BACKGROUND_COLOR to the one produced by fl_contrast:
Fl::set_color(FL_BACKGROUND2_COLOR, yourBgColor);
The same trick works for other hard to reset colors like FL_INACTIVE_COLOR and FL_SELECTION_COLOR.
Hope this workaround helps.

Linux, C, Gtk. Close window after some time

Was trying to code. C, Linux. User enter some string and date. If it's today, then program should show a window with his string. Then this window should be closed by code, not user. And then it will pop up again after a short period of time. gtk_widget_destroy and gtk_widget_hide don't work.
`(aa:26429): Gtk-CRITICAL **: IA__gtk_widget_hide: assertion 'GTK_IS_WIDGET (widget)' failed`
#include <gtk/gtk.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
/* numbered markers placed below by msw for reference */
char str[50];
int main( int argc, char *argv[] )
{
printf("Enter your string\n>:");
fgets( str, 50, stdin );
char time_buf[10], date[10];
int a=strlen(time_buf);
int i=0;
time_t endwait;
time_t start = time(NULL);
time_t seconds = 30;
endwait=start+seconds;
printf("Enter date\n>:");
fgets(date, 10, stdin);
time_t now;
time(&now);
strftime(time_buf, 21, "%Y-%m-%d", gmtime(&now));
if (strncmp(time_buf,date,9) == 0) {
printf("TODAY!\n");
while (start < endwait) {
GtkWidget *label;
GtkWidget *window;
gtk_init( &argc, &argv );
window = gtk_window_new( GTK_WINDOW_TOPLEVEL );
gtk_window_set_title( GTK_WINDOW( window ),"ALARM");
label = gtk_label_new( str );
gtk_container_set_border_width(GTK_CONTAINER(window), 50);
gtk_container_add( GTK_CONTAINER( window ), label );
gtk_widget_show_all( window );
g_signal_connect(G_OBJECT(window),"destroy",
G_CALLBACK( gtk_main_quit ),NULL)
gtk_main(); // mark 1 ###
gtk_widget_destroy (label); // mark 2 ###
//gtk_widget_hide(window);
start = time(NULL);
sleep(10);
}
} else {
printf("NOT TODAY");
return 0;
}
}
GTK has an event loop, started with gtk_main. Actually, it is wrapping a glib event loop, to which you can add timeouts using g_timeout_add_full (or simply g_timeout_add). This is the good way to handle your issue (make some GTK signal handler register a Glib timeout which could e.g. call gtk_widget_hide or gtk_exit ...)
The big problem is that after you invoke gtk_main() at mark 1, your program does not get to run again until gtk_main exits. Prior to gtk_main returning, all widgets from the toplevel down will be destroyed. That's why I assume the error happened at mark 2 where you try to destroy a label that's already been destroyed.
It is still not clear to me why you'd want the behavior. Screen-wide popup notification behaves differently than gtk/glib/X11 programs because it is different. If I am running a program and a top-level window comes to the screen I should know what caused it and be able to get rid of it. It is more intuitive for the user if you scrap the popup, put a label below the entry¹ and change its text to "" when you want it to be invisible. And what #BasileStarynkevitch said.
¹What entry? The one that you should have to enter the string in. it would be an odd program that takes input from the console and displays output in a window.

Programmaticaly check if user is in desktop - Linux

i am making a program which needs to be run in desktop mode. How can I check in C? And also, is it possible to get screen width and height in C(of the monitor)?
This is a very broad question, but I'll bite. I'm assuming that by "desktop mode" you mean a running X window system. Since you don't seem to have a preferred widget toolkit, I'll show an example which uses Xlib.
You could simply try to open the display and check the return value. If it's up, you can retrieve the screen resolution as well:
#include <stdio.h>
#include <X11/Xlib.h>
int main(int argc, char ** argv)
{
int screen_num;
unsigned int display_width, display_height;
Display *display;
/* First connect to the display server, as specified in the DISPLAY
environment variable. */
display = XOpenDisplay(NULL);
if (!display)
{
fprintf(stderr, "unable to connect to display");
return 1;
}
/* pull useful data out of the display object */
screen_num = DefaultScreen(display);
/* Display size is a member of display structure */
display_width = DisplayWidth(display, screen_num);
display_height = DisplayHeight(display, screen_num);
fprintf(stdout, "resolution is %d x %d\n", display_width, display_height);
return 0;
}
You have to compile with -lX11. All of this and much more can be learned from the Xlib programming tutorial here.

How do I get the pixel color under the cursor?

I need a fast command line app to return the color of the pixel under the mouse cursor.
How can I build this in VC++, I need something similar to this, but ideally not in .NET so it can be run many times per second?
Off the top of my head, the straightforward way:
#include <stdio.h>
#include <Windows.h>
int main(void) {
POINT p;
COLORREF color;
HDC hDC;
BOOL b;
// Get the device context for the screen
hDC = GetDC(NULL);
if (hDC == NULL)
return 3;
// Get the current cursor position
b = GetCursorPos(&p);
if (!b)
return 2;
// Retrieve the color at that position
color = GetPixel(hDC, p.x, p.y);
if (color == CLR_INVALID)
return 1;
// Release the device context again
ReleaseDC(GetDesktopWindow(), hDC);
printf("%i %i %i", GetRValue(color), GetGValue(color), GetBValue(color));
return 0;
}
ETA: Appears to work, at least for me.
ETA2: Added some error checking
ETA3: Commented code, compiled executable and a Visual Studio Solution can be found in my SVN repository.

Resources