mirror of
https://github.com/amiwm/amiwm.git
synced 2026-04-14 11:04:18 +00:00
[libami] document how the module loop works; start thinking about how to make it async
* Yeah it'd be nice to just use libevent here, alas * Figure out how all the reading/writing works and comment it * Add a periodic function that'll be called every trip through the main loop; will turn it into something more formally periodic later. There's still lots to do here before I can actually schedule periodic events and the read()s are non-blocking. Give it time!
This commit is contained in:
109
libami/module.c
109
libami/module.c
@@ -8,6 +8,7 @@
|
|||||||
#ifdef HAVE_UNISTD_H
|
#ifdef HAVE_UNISTD_H
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#endif
|
#endif
|
||||||
|
#include <sys/select.h>
|
||||||
|
|
||||||
#include "libami.h"
|
#include "libami.h"
|
||||||
#include "module.h"
|
#include "module.h"
|
||||||
@@ -24,6 +25,7 @@ Window md_root = None;
|
|||||||
static int md_int_len=0;
|
static int md_int_len=0;
|
||||||
static char *md_int_buf=NULL;
|
static char *md_int_buf=NULL;
|
||||||
void (*md_broker_func)(XEvent *, unsigned long);
|
void (*md_broker_func)(XEvent *, unsigned long);
|
||||||
|
void (*md_periodic_func)(void);
|
||||||
|
|
||||||
void md_exit(int signal)
|
void md_exit(int signal)
|
||||||
{
|
{
|
||||||
@@ -54,6 +56,12 @@ static int md_write(void *ptr, int len)
|
|||||||
return tot;
|
return tot;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Read from the input file descriptor until len; ,populate
|
||||||
|
* our buffer.
|
||||||
|
*
|
||||||
|
* Return total read, else -1 on error.
|
||||||
|
*/
|
||||||
static int md_read(void *ptr, int len)
|
static int md_read(void *ptr, int len)
|
||||||
{
|
{
|
||||||
char *p=ptr;
|
char *p=ptr;
|
||||||
@@ -78,6 +86,10 @@ static int md_read(void *ptr, int len)
|
|||||||
return tot;
|
return tot;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Read in "len" bytes from the window manager command path
|
||||||
|
* into md_int_buf.
|
||||||
|
*/
|
||||||
static int md_int_load(int len)
|
static int md_int_load(int len)
|
||||||
{
|
{
|
||||||
if(len>=md_int_len) {
|
if(len>=md_int_len) {
|
||||||
@@ -96,6 +108,13 @@ static struct md_queued_event {
|
|||||||
struct mcmd_event e;
|
struct mcmd_event e;
|
||||||
} *event_head=NULL, *event_tail=NULL;
|
} *event_head=NULL, *event_tail=NULL;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Process queued XEvents from the window manager.
|
||||||
|
*
|
||||||
|
* The window manager pushes subscribed XEvents down to
|
||||||
|
* modules and this pulls them out of the queue and
|
||||||
|
* calls md_broker_func() on each of them.
|
||||||
|
*/
|
||||||
void md_process_queued_events()
|
void md_process_queued_events()
|
||||||
{
|
{
|
||||||
struct md_queued_event *e;
|
struct md_queued_event *e;
|
||||||
@@ -107,6 +126,12 @@ void md_process_queued_events()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Enqueue an mcmd event into the event queue.
|
||||||
|
*
|
||||||
|
* This is called when there's an XEvent being queued
|
||||||
|
* from the window manager to the module.
|
||||||
|
*/
|
||||||
static void md_enqueue(struct mcmd_event *e)
|
static void md_enqueue(struct mcmd_event *e)
|
||||||
{
|
{
|
||||||
struct md_queued_event *qe=malloc(sizeof(struct md_queued_event));
|
struct md_queued_event *qe=malloc(sizeof(struct md_queued_event));
|
||||||
@@ -122,6 +147,13 @@ static void md_enqueue(struct mcmd_event *e)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Read an async XEvent from the window manager.
|
||||||
|
*
|
||||||
|
* This is called by md_handle_input() to read an XEvent.
|
||||||
|
* The "I'm an Xevent" marker is ~len, so it's de-inverted
|
||||||
|
* and then a subsequent len field match must match it.
|
||||||
|
*/
|
||||||
static int md_get_async(int len)
|
static int md_get_async(int len)
|
||||||
{
|
{
|
||||||
if(md_int_load(len)!=len)
|
if(md_int_load(len)!=len)
|
||||||
@@ -131,18 +163,30 @@ static int md_get_async(int len)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Read input from the window manager.
|
||||||
|
*
|
||||||
|
* This reads two chunks - the size of the request,
|
||||||
|
* and then the request itself.
|
||||||
|
*
|
||||||
|
* Negative request lengths are treated special - they're
|
||||||
|
* treated as XEvents thrown into the input stream.
|
||||||
|
*/
|
||||||
int md_handle_input()
|
int md_handle_input()
|
||||||
{
|
{
|
||||||
int res;
|
int res;
|
||||||
|
|
||||||
|
/* Read the length of the request */
|
||||||
if(md_read(&res, sizeof(res))!=sizeof(res))
|
if(md_read(&res, sizeof(res))!=sizeof(res))
|
||||||
return -1;
|
return -1;
|
||||||
if(res>=0) {
|
if(res>=0) {
|
||||||
if(!res)
|
if(!res)
|
||||||
return 0;
|
return 0;
|
||||||
|
/* Read the command */
|
||||||
md_int_load(res);
|
md_int_load(res);
|
||||||
return 0;
|
return 0;
|
||||||
} else {
|
} else {
|
||||||
|
/* Negative length; treat as an XEvent */
|
||||||
res=~res;
|
res=~res;
|
||||||
if(!res)
|
if(!res)
|
||||||
return 0;
|
return 0;
|
||||||
@@ -150,6 +194,16 @@ int md_handle_input()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Send a command from the module back to the window manager.
|
||||||
|
*
|
||||||
|
* This sends a request up to the window manager and then reads the
|
||||||
|
* response to return. If asynchronous XEvents occur in the reply
|
||||||
|
* stream then those are enqueued via md_get_async().
|
||||||
|
*
|
||||||
|
* If there is a response, buffer is set to a memory buffer containing it.
|
||||||
|
* It is thus up to the caller to free it.
|
||||||
|
*/
|
||||||
int md_command(XID id, int cmd, void *data, int data_len, char **buffer)
|
int md_command(XID id, int cmd, void *data, int data_len, char **buffer)
|
||||||
{
|
{
|
||||||
int res;
|
int res;
|
||||||
@@ -161,21 +215,38 @@ int md_command(XID id, int cmd, void *data, int data_len, char **buffer)
|
|||||||
mcmd.cmd = cmd;
|
mcmd.cmd = cmd;
|
||||||
mcmd.len = data_len;
|
mcmd.len = data_len;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Send header, read response code.
|
||||||
|
*/
|
||||||
if(md_write(&mcmd, sizeof(mcmd))!=sizeof(mcmd) ||
|
if(md_write(&mcmd, sizeof(mcmd))!=sizeof(mcmd) ||
|
||||||
md_write(data, data_len)!=data_len ||
|
md_write(data, data_len)!=data_len ||
|
||||||
md_read(&res, sizeof(res))!=sizeof(res))
|
md_read(&res, sizeof(res))!=sizeof(res))
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If the response code is negative (well, less than -1)
|
||||||
|
* then its treated as an async XEvent. So, queue that
|
||||||
|
* and keep reading for the response code.
|
||||||
|
*/
|
||||||
while(res<-1) {
|
while(res<-1) {
|
||||||
md_get_async(~res);
|
md_get_async(~res);
|
||||||
if(md_read(&res, sizeof(res))!=sizeof(res))
|
if(md_read(&res, sizeof(res))!=sizeof(res))
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If the response code is >0, then allocate a buffer
|
||||||
|
* of a suitable size and read the response into the buffer.
|
||||||
|
*/
|
||||||
if(res>0) {
|
if(res>0) {
|
||||||
*buffer=malloc(res);
|
*buffer=malloc(res);
|
||||||
if(md_read(*buffer, res)!=res)
|
if(md_read(*buffer, res)!=res)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return the response size.
|
||||||
|
*/
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -230,9 +301,43 @@ char *md_init(int argc, char *argv[])
|
|||||||
return (argc>4? argv[4]:NULL);
|
return (argc>4? argv[4]:NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
void md_main_loop()
|
void
|
||||||
|
md_main_loop()
|
||||||
{
|
{
|
||||||
do md_process_queued_events(); while(md_handle_input()>=0);
|
fd_set readfds;
|
||||||
|
struct timeval tv;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* For now I'm going to use select() for up to
|
||||||
|
* one second on the input FD, even though the
|
||||||
|
* FD is blocking.
|
||||||
|
*
|
||||||
|
* That way in the main loop we at least will
|
||||||
|
* get the chance to run the periodic function.
|
||||||
|
*
|
||||||
|
* A module can then for now set its own timer
|
||||||
|
* signal to run, which should interrupt this
|
||||||
|
* select.
|
||||||
|
*/
|
||||||
|
do {
|
||||||
|
FD_ZERO(&readfds);
|
||||||
|
FD_SET(md_in_fd, &readfds);
|
||||||
|
tv.tv_sec = 1;
|
||||||
|
tv.tv_usec = 0;
|
||||||
|
|
||||||
|
ret = select(md_in_fd + 1, &readfds, NULL, NULL, &tv);
|
||||||
|
(void) ret;
|
||||||
|
|
||||||
|
if (md_periodic_func != NULL) {
|
||||||
|
md_periodic_func();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Process async XEvent events that have been read */
|
||||||
|
md_process_queued_events();
|
||||||
|
|
||||||
|
/* Loop over, reading input events */
|
||||||
|
} while(md_handle_input()>=0);
|
||||||
}
|
}
|
||||||
|
|
||||||
int md_connection_number()
|
int md_connection_number()
|
||||||
|
|||||||
Reference in New Issue
Block a user