What's the best way of waiting a finite a mount of time for an expose event on X, then waking up and doing a redraw, even if not expose event has been received? The purpose is to have an opengl animation running at sometimes where at others I simply want to redraw if needed. Here is my code as I have it now, check below for pseudo-code of what I'm looking for:
do {
XNextEvent(dpy, &event);
switch(event.type) {
...
case Expose:
need_redraw = True;
break;
}
} while(XPending(dpy)); /* loop to compress events */
if ( need_redraw )
{
// do redraw
}
And this is a pseudo-example of what I would like:
bool animation_enabled = true;
XPostTimeoutEventEvery( 0.3 ); // <-- X will send a "Timeout"
// event each 0.3 seconds.
do {
XNextEvent(dpy, &event);
switch(event.type) {
...
case Expose:
// Redraw if it is required
need_redraw = True;
break;
// -- here --
case Timeout:
// Otherwise, after 0.3 seconds, redraw anyway if
// the animation is running
if ( animation_enabled )
{
need_redraw = True;
}
break;
}
} while(XPending(dpy)); /* loop to compress events */
if ( need_redraw )
{
// do redraw
// potentially change "animation_enabled" value
}
Just use a regular system timer; if a desired event doesn't arrive in time, just do whatever you want to do.
X is not an application framework, it's a display protocol. Timers are outside (of that) scope of X11.
Check here and the link provided in that answer.
XLib does not offer a "timed out" version of XNextEvent.
But, a timed out version can be easily implemented.
You will need a function
that checks if a file has been updated
within a given timeout,
you can implement it using select:
#include <sys/select.h>
static int wait_fd(int fd, double seconds)
{
struct timeval tv;
fd_set in_fds;
FD_ZERO(&in_fds);
FD_SET(fd, &in_fds);
tv.tv_sec = trunc(seconds);
tv.tv_usec = (seconds - trunc(seconds))*1000000;
return select(fd+1, &in_fds, 0, 0, &tv);
}
Then, you can use wait_fd
in the file descriptor returned by
ConnectionNumber(display)
to wait for an event
within a given time limit:
int XNextEventTimeout(Display *display, XEvent *event, double seconds)
{
if (XPending(display) || wait_fd(ConnectionNumber(display),seconds)) {
XNextEvent(display, event);
return 0;
} else {
return 1;
}
}
In your main loop,
you can use the XNextEventTimeout function
to wait for events within a given timeout.
If the timeout expires, you can simulate the desired event,
in you case an Expose event:
for (;;) {
if (XNextEventTimeout(dpy, &event, 1.)) {
/* Handle timeout "event"
* one option is to simulate an Expose event */
e.type = Expose;
e.xexpose.count = 0;
}
switch (event.type) {
case Expose:
/* Handle expose event */
break;
/* ... */
/* Handle other events */
}
}
A simpler solution is to use the non-blocking Xlib equivalents to XNextEvent. Here's what I use to check for X events each time through the frame loop:
mask = KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask;
while (XCheckWindowEvent(xDisplay, xWin, mask, &evt) ||
XCheckTypedWindowEvent(xDisplay, xWin, ClientMessage, &evt)) {
/* Handle event */
}
Hope this helps. The full code is in my demo OpenGL/GLX program
http://cs.anu.edu.au/~Hugh.Fisher/3dteach/glxcube.tar
Related
I want to put a string or bytes to the X11 selection so that when I switch to other applications, I can directly do a ctrl+p paste.
I try to follow the documentation of the X11 clipboard mechanism. If I understand correctly, I need to use XSetSelectionOwner to obtain the XA_CLIPBOARD selection and then use XChangeProperty to put my data to the clipboard.
Here is a simple snippet, but unfortunately it does not work:
// main.c
#include <stdlib.h>
#include <stdio.h>
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <X11/Xmu/Atoms.h>
int main() {
// try to write `hello` to the clipboard
const char *in = "hello\0";
const int n = 5;
Display* d = XOpenDisplay(0);
Window w = XCreateSimpleWindow(d, DefaultRootWindow(d), 0, 0, 1, 1, 0, 0, 0);
Atom XA_CLIPBOARD = XInternAtom(d, "CLIPBOARD", True);
XSetSelectionOwner(d, XA_CLIPBOARD, w, CurrentTime);
XEvent event;
XNextEvent(d, &event);
if (event.type != SelectionRequest) {
XCloseDisplay(d);
return 0;
}
if (event.xselectionrequest.selection != XA_CLIPBOARD) {
XCloseDisplay(d);
return 0;
}
XSelectionRequestEvent* req = &event.xselectionrequest;
XChangeProperty(d, req->requestor, req->property, XA_STRING, 8, PropModeReplace, (unsigned char *)in, n);
XEvent re = {0};
re.xselection.type = SelectionNotify;
re.xselection.display = req->display;
re.xselection.requestor = req->requestor;
re.xselection.selection = req->selection;
re.xselection.property = req->property;
re.xselection.target = req->target;
XSendEvent(d, req->requestor, 0, 0, &re); // event is sent, but data is not in my clipboard
XFlush(d);
XCloseDisplay(d);
return 0;
}
Compile: clang -o main main.c -lX11 -lXmu
What did I do wrong, and how to fix it?
XNextEvent retrieves any event that occured (input, pointer, configuration, property changes, ...).
So it is very unlikely that the first event, will be your desired event.
You have to iterate over the event queue to check if a SelectionRequest event occured.
Therefore you have to call XNextEvent in a loop and check everytime the event.type for the desired event.
Edit:
If you retrieve a ClientMessage event and the client data equals the Atom WM_DELETE, there was a request by the user to close the window (pressing the X). Other than that, you can quit whenever you want.
XEvent evt;
while (i_want_to_receive_and_process_events) {
XNextEvent(dpy, &evt);
switch (evt.type) {
case SelectionRequest:
if (evt.xselectionrequest.selection == XA_CLIPBOARD) {
// what you were looking for, do your thing
// i_want_to_receive_and_process_events = false (?)
}
break;
case ClientMessage:
if (evt.xclient.data.l[0] == WM_DELETE) {
i_want_to_receive_and_process_events = false;
}
break;
}
}
I wrote a simple 2d tile based game and ran it on 2 computers. On comp1 it's very slow, on comp2 it's lightening fast.
Comp1's cpu is a little slower, but it can play other sdl games (even inside emulaters) and simple 3d games, fast enough.
So I ran the game on both computers using only cpu rendering: both ran at the same speed.
Then I switched to gpu rendering and the 2nd computer went really fast (200+fps) but the first stayed at the same pace, around 20fps.
So I assumed either the hw acceleration is not working on comp1 or the cpu is clogged with my calculations.
I logged all the error handling and can confirm both computers support the hw acceleration and the texture sizes (i know this for a fact because the cpu bound version of the game has no flip and so the character always faces the same way ;)
So then it has to be a slow cpu on the first computer, right? But when I check the cpu stats the game only takes 1,2% cpu, and overall load is around 2% cpu
So the cpu is sitting mostly idle while my game crawls along :(
The game is a few 1000 lines of code now, so here are some of the statements that I think pertain to this problem:
Main game loop:
while (!quit)
{
GameEngine_Tick(gameType->fps, false, 0);
if (SDL_PollEvent(&event) != 0))
{
int windowID = event.window.windowID;
if (event.window.event == SDL_WINDOWEVENT_CLOSE &&
event.window.windowID == screens[0].windowID) quit = true;
switch (event.type)
{
case SDL_KEYDOWN:
if (event.key.keysym.sym == SDLK_LEFT) { Key_Left(); }
if (event.key.keysym.sym == SDLK_RIGHT) { Key_Right();}
if (event.key.keysym.sym == SDLK_UP) { Key_Up(); }
if (event.key.keysym.sym == SDLK_DOWN) { Key_Down(); }
break; // end key_down.
} // switch event type
}
GameEngine_Render();
}
Frame rate limiting in GameEngine_Tick:
void GameEngine_Tick(int fps, bool bIsRegTestStep, Uint8 TDir)
{
unsigned int frametime = 1000 / fps;
unsigned int currentTime;
const Uint8* keystates = SDL_GetKeyboardState(NULL); // do not free.
SDL_PumpEvents(); // upd keystates to get keyboard inputs.
// Get KB input - todo: later GetInputFromSelectedInputDevice (checks kb/xbox etc)
if (keystates[SDL_SCANCODE_LEFT]) GameLeft();
else if (keystates[SDL_SCANCODE_RIGHT]) GameRight();
Sprites_Tick();
// set framerate by waiting if needed.
currentTime = SDL_GetTicks();
if (currentTime - prevTime < (frametime))
{
//Sleep the remaining frame time
if (fps > 0) SDL_Delay((frametime)-(currentTime - prevTime)); /* fps-1 == MAX == no delay*/
}
prevTime = currentTime;
}
create renderer:
ren = nullptr;
if (bForceCPURender == false) ren = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);// | SDL_RENDERER_PRESENTVSYNC);
if (nullptr == ren)
{
if (bForceCPURender == false)
{
Log("Info: SDL_CreateRenderer with SDL_RENDERER_ACCELERATED failed. Trying default.");
}
bForceCPURender = true;
ren = SDL_CreateRenderer(window, -1, 0);
if (nullptr == ren)
{
Log("Info: SDL_CreateRenderer with default also failed.");
SDLDestroy();
return -1;
}
else Logs("Info: success using default renderer.");
}
else Logs("Info: success, using SDL_CreateRenderer with SDL_RENDERER_ACCELERATED.");
Creating textures or surfaces:
spriteMapTexture = SDL_CreateTexture(ren, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET,
x_pixPerCharInStageMap * roomWidthInChars * gameRoomMatrix_w,
y_pixPerCharInStageMap * roomHeightInChars * gameRoomMatrix_h);
// If GPU cannot handle size of stage map: try to do CPU preRendering. (gpu is texture, cpu is surface).
if (NULL == spriteMapTexture || bForceCPUStageRenderAnyway == true)
{
spriteMapSurface = GameEngine_CreateRGBSurface(x_pixPerCharInStageMap * roomWidthInChars * gameRoomMatrix_w, y_pixPerCharInStageMap * roomHeightInChars * gameRoomMatrix_h);
}
while (!quit)
{
GameEngine_Tick(gameType->fps, false, 0);
if (SDL_PollEvent(&event) != 0))
^^ wat
{
// process event
}
GameEngine_Render();
}
Right now you're only processing a single event each frame. Used to be SDL could only buffer 128 events before dropping them, nowadays I think they're up to 64k. So if, for example, you have a high-report-rate gaming mouse (500-1000 Hz sampling) your event queue will get backed up far faster than a regular mouse (125 Hz). Same for any other event type SDL can generate; some systems will have different event generation rates than others.
You want to process every event in the queue before rendering a frame:
while (!quit)
{
GameEngine_Tick(gameType->fps, false, 0);
// NOTE: if -> while
while( SDL_PollEvent(&event) )
{
// process event
}
GameEngine_Render();
}
I want to make a process with 3 threads. Out of which, I want one thread to work once in every 50ms. So made 2 threads to do my other works and in the third thread I initialised a timer. When I did so the synchronisation between the threads doest seem that good. I cant find the timer codes executing in every 50ms. Its random in nature. The code brief is shown below. Thanks in advance.
void * vUserInterfaceThread()
{
while(1)
{
//***doing my interface code here***********/
}
}
void * vMornitorThread()
{
while(1)
{
//***doing my monitor code here***********/
}
}
void * vTimerThread()
{
vStartTimer(ENABLE); // enabled the timer with 50ms delay with the function
while(1);
}
void vTimerFunction()
{
//******Code to be executed in every 50ms time duration here************//
}
void vStartTimer(unsigned char ucValue)
{
if(ucValue == ENABLE)
{
memset (&sSigActionStruct, 0, sizeof (sSigActionStruct));
sSigActionStruct.sa_handler = &vTimerHandler;
sigaction (SIGVTALRM, &sSigActionStruct, NULL);
iTimerValue.it_value.tv_sec = 0;
iTimerValue.it_value.tv_usec = TIMERLOADVALUE; //Load value for 50ms
iTimerValue.it_interval.tv_sec = 0;
iTimerValue.it_interval.tv_usec = TIMERLOADVALUE; //Load value for 50ms
setitimer (ITIMER_VIRTUAL/*ITIMER_REAL*/, &iTimerValue, NULL);
}
}
int main(void)
{
//***************doing other initialisations***************************//
pthread_create(&pThreadID1,NULL,vUserInterfaceThread,NULL);
pthread_create(&pThreadID2,NULL,vMornitorThread,NULL);
pthread_create(&pThreadID3,NULL,vTimerThread,NULL);
pthread_join(pThreadID1,NULL);
pthread_join(pThreadID2,NULL);
pthread_join(pThreadID3,NULL);
}
To answer a part of your question:
If you want to adjust your thread priorities you can use pthread_attr_setschedparam
pthread_attr_t thread_attributes;
pthread_attr_init(&thread_attributes);
struct sched_param params = {.sched_priority = 15}; // Set higher/lower priorities in other threads
pthread_attr_setschedparam(&thread_attributes, ¶ms);
pthread_create(&pThreadID1, &thread_attributes, vUserInterfaceThread, NULL);
This is my seeking function:
gboolean seek(CustomData* data)
{
gint64 position;
GstFormat format = GST_FORMAT_TIME;
GstEvent *seek_event;
/* Obtain the current position, needed for the seek event */
if (!gst_element_query_position(data->pipeline, &format, &position))
{
g_printerr("Unable to retrieve current position.\n");
return FALSE;
}
/* Create the seek event */
if (data->rate > 0)
{
seek_event = gst_event_new_seek(data->rate, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE, GST_SEEK_TYPE_SET,
position, GST_SEEK_TYPE_NONE, 0);
}
else if (data->rate < 0)
{
seek_event = gst_event_new_seek(data->rate, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE, GST_SEEK_TYPE_SET, 0,
GST_SEEK_TYPE_SET, position);
}
else
{
g_printerr("Rate is set to 0.\n");
return FALSE;
}
/* Check that seek_event was created */
if (seek_event == NULL) {
g_printerr("Could not create seek event.\n");
return FALSE;
}
/* Send the event */
if (!gst_element_send_event(data->autovideosink, seek_event))
{
g_printerr("Could not perform seek event.\n");
return FALSE;
}
g_print("Current rate: %gx\n", data->rate);
return TRUE;
}
But it fails at sending the seek event. This code is pertty much just slightly modified from the GStreamer tutorials, but I'm playing a .vob file and I have a custom pipeline instead of playbin2. I'm also using appsrc so I'm feeding the buffers from a file, but I don't imagine that would cause any problems with fast forwarding. However, I can't seek either forward or backward (setting the rate to 2x or .5x fails at the same spot).
I have same problem and I've found this on debug output:
0:00:49.048266933 4480 03B05000 DEBUG basesrc gstbasesrc.c:1972:gst_base_src_default_event:<app_source> is not seekable
0:00:49.048386221 4480 03B05000 DEBUG basesrc gstbasesrc.c:2000:gst_base_src_event:<app_source> subclass refused event
0:00:49.048515238 4480 03B05000 DEBUG GST_PADS gstpad.c:5050:gst_pad_send_event_unchecked:<app_source:src> sent event, ret error
It looks like appsrc element does not support seeking or event is sending upstream instead downstream.
I have a lightweight application that catches Xorg and dbus events. In order to do this I initialized dbus loop and started g_main_loop, but I don't know how to add Xorg event handling in a natural way:
GMainLoop * mainloop = NULL;
mainloop = g_main_loop_new(NULL,FALSE);
dbus_g_thread_init ();
dbus_init();
// <<<<<<<<<<<<<<<<<<<<<<<<<
//1 way using timeout
//g_timeout_add(100, kbdd_default_iter, mainloop);
//2nd way using pthread
//GThread * t = g_thread_create(kbdd_default_loop, NULL, FALSE, NULL);
//>>>>>>>>>>>>>>>>>>>>>>>>>>>
g_main_loop_run(mainloop);
in default iter I'm checking if there is waiting X-event and handle them.
Both ways seems bad, first because I have unneeded calls for checking event, second because I make an additional thread and have to make additional locks.
P.S. I know I can use gtk lib, but I don't want to have dependencies on any toolkit.
If you want to add Xorg event handling to the main loop without using a timeout (which as you state is wasteful), you'll need to add a source that polls the X connection. For that, you'll need to get below the Xlib abstraction layer to get the underlying X connection file descriptor. That's what the complete program below does. It is an adaptation of C. Tronche's excellent X11 tutorial to use the glib main loop for polling. I also drew from "Foundations of GTK+ Development" by Andrew Krause.
If this doesn't seem very "natural", that's because I doubt there is a very "natural" way to do this - you're really re-implementing a core part of GDK here.
/* needed to break into 'Display' struct internals. */
#define XLIB_ILLEGAL_ACCESS
#include <X11/Xlib.h> // Every Xlib program must include this
#include <assert.h> // I include this to test return values the lazy way
#include <glib.h>
typedef struct _x11_source {
GSource source;
Display *dpy;
Window w;
} x11_source_t;
static gboolean
x11_fd_prepare(GSource *source,
gint *timeout)
{
*timeout = -1;
return FALSE;
}
static gboolean
x11_fd_check (GSource *source)
{
return TRUE;
}
static gboolean
x11_fd_dispatch(GSource* source, GSourceFunc callback, gpointer user_data)
{
static gint counter = 0;
Display *dpy = ((x11_source_t*)source)->dpy;
Window window = ((x11_source_t*)source)->w;
XEvent e;
while (XCheckWindowEvent(dpy,
window,
EnterWindowMask,
&e))
{
if (e.type == EnterNotify)
g_print("We're in!!! (%d)\n", ++counter);
}
return TRUE;
}
static gboolean
msg_beacon(gpointer data)
{
static gint counter = 0;
g_print("Beacon %d\n", ++counter);
return TRUE;
}
int
main()
{
Display *dpy = XOpenDisplay(NULL);
assert(dpy);
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);
XSelectInput(dpy, w, StructureNotifyMask | EnterWindowMask);
XMapWindow(dpy, w);
for (;;) {
XEvent e;
XNextEvent(dpy, &e);
if (e.type == MapNotify)
break;
}
GMainLoop *mainloop = NULL;
mainloop = g_main_loop_new(NULL, FALSE);
/* beacon to demonstrate we're not blocked. */
g_timeout_add(300, msg_beacon, mainloop);
GPollFD dpy_pollfd = {dpy->fd,
G_IO_IN | G_IO_HUP | G_IO_ERR,
0};
GSourceFuncs x11_source_funcs = {
x11_fd_prepare,
x11_fd_check,
x11_fd_dispatch,
NULL, /* finalize */
NULL, /* closure_callback */
NULL /* closure_marshal */
};
GSource *x11_source =
g_source_new(&x11_source_funcs, sizeof(x11_source_t));
((x11_source_t*)x11_source)->dpy = dpy;
((x11_source_t*)x11_source)->w = w;
g_source_add_poll(x11_source, &dpy_pollfd);
g_source_attach(x11_source, NULL);
g_main_loop_run(mainloop);
return 0;
}