tint2: Do not rely only on select() to detect new X events, since we sync the socket in the system tray which messes up select

This commit is contained in:
o9000 2015-06-25 15:36:47 +02:00
parent 222eca4099
commit 4d69a0bc33

View file

@ -224,9 +224,11 @@ void init_X11_post_config()
if (pipe(sn_pipe) != 0) { if (pipe(sn_pipe) != 0) {
fprintf(stderr, "Creating pipe failed.\n"); fprintf(stderr, "Creating pipe failed.\n");
} else { } else {
fcntl(sn_pipe[0], F_SETFL, O_NONBLOCK | fcntl(sn_pipe[0], F_GETFL));
fcntl(sn_pipe[1], F_SETFL, O_NONBLOCK | fcntl(sn_pipe[1], F_GETFL));
sn_pipe_valid = 1; sn_pipe_valid = 1;
struct sigaction act; struct sigaction act;
memset (&act, 0, sizeof (struct sigaction)); memset(&act, 0, sizeof(struct sigaction));
act.sa_handler = sigchld_handler; act.sa_handler = sigchld_handler;
if (sigaction(SIGCHLD, &act, 0)) { if (sigaction(SIGCHLD, &act, 0)) {
perror("sigaction"); perror("sigaction");
@ -1173,245 +1175,247 @@ start:
timeout = 0; timeout = 0;
// Wait for X Event or a Timer // Wait for X Event or a Timer
if (select(maxfd+1, &fdset, 0, 0, timeout) > 0) { if (systray_profile)
if (FD_ISSET(sn_pipe[0], &fdset)) { fprintf(stderr, "[%f] %s:%d waiting for events...\n", profiling_get_time(), __FUNCTION__, __LINE__);
if (XPending(server.dsp) > 0 || select(maxfd+1, &fdset, 0, 0, timeout) >= 0) {
if (systray_profile)
fprintf(stderr, "[%f] %s:%d processing events\n", profiling_get_time(), __FUNCTION__, __LINE__);
if (sn_pipe_valid) {
char buffer[1]; char buffer[1];
ssize_t wur = read(sn_pipe[0], buffer, 1); while (read(sn_pipe[0], buffer, sizeof(buffer)) > 0) {
(void) wur; sigchld_handler_async();
sigchld_handler_async(); }
} }
if (FD_ISSET(x11_fd, &fdset)) { while (XPending(server.dsp)) {
while (XPending (server.dsp)) { XNextEvent(server.dsp, &e);
XNextEvent(server.dsp, &e);
#if HAVE_SN #if HAVE_SN
if (startup_notifications) if (startup_notifications)
sn_display_process_event(server.sn_dsp, &e); sn_display_process_event(server.sn_dsp, &e);
#endif // HAVE_SN #endif // HAVE_SN
panel = get_panel(e.xany.window); panel = get_panel(e.xany.window);
if (panel && panel_autohide) { if (panel && panel_autohide) {
if (e.type == EnterNotify) if (e.type == EnterNotify)
autohide_trigger_show(panel); autohide_trigger_show(panel);
else if (e.type == LeaveNotify) else if (e.type == LeaveNotify)
autohide_trigger_hide(panel); autohide_trigger_hide(panel);
if (panel->is_hidden) { if (panel->is_hidden) {
if (e.type == ClientMessage && e.xclient.message_type == server.atom.XdndPosition) { if (e.type == ClientMessage && e.xclient.message_type == server.atom.XdndPosition) {
hidden_dnd = 1; hidden_dnd = 1;
autohide_show(panel); autohide_show(panel);
}
else
continue; // discard further processing of this event because the panel is not visible yet
} }
else if (hidden_dnd && e.type == ClientMessage && e.xclient.message_type == server.atom.XdndLeave) {
hidden_dnd = 0;
autohide_hide(panel);
}
}
switch (e.type) {
case ButtonPress:
tooltip_hide(0);
event_button_press (&e);
break;
case ButtonRelease:
event_button_release(&e);
break;
case MotionNotify: {
unsigned int button_mask = Button1Mask | Button2Mask | Button3Mask | Button4Mask | Button5Mask;
if (e.xmotion.state & button_mask)
event_button_motion_notify (&e);
Panel* panel = get_panel(e.xmotion.window);
Area* area = click_area(panel, e.xmotion.x, e.xmotion.y);
if (area->_get_tooltip_text)
tooltip_trigger_show(area, panel, &e);
else else
tooltip_trigger_hide(); continue; // discard further processing of this event because the panel is not visible yet
}
else if (hidden_dnd && e.type == ClientMessage && e.xclient.message_type == server.atom.XdndLeave) {
hidden_dnd = 0;
autohide_hide(panel);
}
}
switch (e.type) {
case ButtonPress:
tooltip_hide(0);
event_button_press (&e);
break;
case ButtonRelease:
event_button_release(&e);
break;
case MotionNotify: {
unsigned int button_mask = Button1Mask | Button2Mask | Button3Mask | Button4Mask | Button5Mask;
if (e.xmotion.state & button_mask)
event_button_motion_notify (&e);
Panel* panel = get_panel(e.xmotion.window);
Area* area = click_area(panel, e.xmotion.x, e.xmotion.y);
if (area->_get_tooltip_text)
tooltip_trigger_show(area, panel, &e);
else
tooltip_trigger_hide();
break;
}
case LeaveNotify:
tooltip_trigger_hide();
break;
case Expose:
event_expose(&e);
break;
case PropertyNotify:
event_property_notify(&e);
break;
case ConfigureNotify:
event_configure_notify(&e);
break;
case ReparentNotify:
if (!systray_enabled)
break;
panel = (Panel*)systray.area.panel;
if (e.xany.window == panel->main_win) // reparented to us
break;
// FIXME: 'reparent to us' badly detected => disabled
break;
case UnmapNotify:
case DestroyNotify:
if (e.xany.window == server.composite_manager) {
// Stop real_transparency
signal_pending = SIGUSR1;
break; break;
} }
if (e.xany.window == g_tooltip.window || !systray_enabled)
case LeaveNotify:
tooltip_trigger_hide();
break; break;
for (it = systray.list_icons; it; it = g_slist_next(it)) {
case Expose: if (((TrayWindow*)it->data)->win == e.xany.window) {
event_expose(&e); systray_destroy_event((TrayWindow*)it->data);
break;
case PropertyNotify:
event_property_notify(&e);
break;
case ConfigureNotify:
event_configure_notify(&e);
break;
case ReparentNotify:
if (!systray_enabled)
break; break;
panel = (Panel*)systray.area.panel; }
if (e.xany.window == panel->main_win) // reparented to us }
break; break;
// FIXME: 'reparent to us' badly detected => disabled
break; case ClientMessage:
case UnmapNotify: ev = &e.xclient;
case DestroyNotify: if (ev->data.l[1] == server.atom._NET_WM_CM_S0) {
if (e.xany.window == server.composite_manager) { if (ev->data.l[2] == None)
// Stop real_transparency // Stop real_transparency
signal_pending = SIGUSR1; signal_pending = SIGUSR1;
break; else
} // Start real_transparency
if (e.xany.window == g_tooltip.window || !systray_enabled) signal_pending = SIGUSR1;
break; }
for (it = systray.list_icons; it; it = g_slist_next(it)) { if (systray_enabled && e.xclient.message_type == server.atom._NET_SYSTEM_TRAY_OPCODE && e.xclient.format == 32 && e.xclient.window == net_sel_win) {
if (((TrayWindow*)it->data)->win == e.xany.window) { net_message(&e.xclient);
systray_destroy_event((TrayWindow*)it->data); }
break; else if (e.xclient.message_type == server.atom.XdndEnter) {
dnd_enter(&e.xclient);
}
else if (e.xclient.message_type == server.atom.XdndPosition) {
dnd_position(&e.xclient);
}
else if (e.xclient.message_type == server.atom.XdndDrop) {
dnd_drop(&e.xclient);
}
break;
case SelectionNotify:
{
Atom target = e.xselection.target;
fprintf(stderr, "DnD %s:%d: A selection notify has arrived!\n", __FILE__, __LINE__);
fprintf(stderr, "DnD %s:%d: Requestor = %lu\n", __FILE__, __LINE__, e.xselectionrequest.requestor);
fprintf(stderr, "DnD %s:%d: Selection atom = %s\n", __FILE__, __LINE__, GetAtomName(server.dsp, e.xselection.selection));
fprintf(stderr, "DnD %s:%d: Target atom = %s\n", __FILE__, __LINE__, GetAtomName(server.dsp, target));
fprintf(stderr, "DnD %s:%d: Property atom = %s\n", __FILE__, __LINE__, GetAtomName(server.dsp, e.xselection.property));
if (e.xselection.property != None && dnd_launcher_exec) {
Property prop = read_property(server.dsp, dnd_target_window, dnd_selection);
//If we're being given a list of targets (possible conversions)
if (target == server.atom.TARGETS && !dnd_sent_request) {
dnd_sent_request = 1;
dnd_atom = pick_target_from_targets(server.dsp, prop);
if (dnd_atom == None) {
fprintf(stderr, "No matching datatypes.\n");
} else {
//Request the data type we are able to select
fprintf(stderr, "Now requsting type %s", GetAtomName(server.dsp, dnd_atom));
XConvertSelection(server.dsp, dnd_selection, dnd_atom, dnd_selection, dnd_target_window, CurrentTime);
} }
} } else if (target == dnd_atom) {
break; //Dump the binary data
fprintf(stderr, "DnD %s:%d: Data begins:\n", __FILE__, __LINE__);
fprintf(stderr, "--------\n");
int i;
for (i = 0; i < prop.nitems * prop.format/8; i++)
fprintf(stderr, "%c", ((char*)prop.data)[i]);
fprintf(stderr, "--------\n");
case ClientMessage: int cmd_length = 0;
ev = &e.xclient; cmd_length += 1; // (
if (ev->data.l[1] == server.atom._NET_WM_CM_S0) { cmd_length += strlen(dnd_launcher_exec) + 1; // exec + space
if (ev->data.l[2] == None) cmd_length += 1; // open double quotes
// Stop real_transparency for (i = 0; i < prop.nitems * prop.format/8; i++) {
signal_pending = SIGUSR1; char c = ((char*)prop.data)[i];
else if (c == '\n') {
// Start real_transparency if (i < prop.nitems * prop.format/8 - 1) {
signal_pending = SIGUSR1; cmd_length += 3; // close double quotes, space, open double quotes
} }
if (systray_enabled && e.xclient.message_type == server.atom._NET_SYSTEM_TRAY_OPCODE && e.xclient.format == 32 && e.xclient.window == net_sel_win) { } else if (c == '\r') {
net_message(&e.xclient);
}
else if (e.xclient.message_type == server.atom.XdndEnter) {
dnd_enter(&e.xclient);
}
else if (e.xclient.message_type == server.atom.XdndPosition) {
dnd_position(&e.xclient);
}
else if (e.xclient.message_type == server.atom.XdndDrop) {
dnd_drop(&e.xclient);
}
break;
case SelectionNotify:
{
Atom target = e.xselection.target;
fprintf(stderr, "DnD %s:%d: A selection notify has arrived!\n", __FILE__, __LINE__);
fprintf(stderr, "DnD %s:%d: Requestor = %lu\n", __FILE__, __LINE__, e.xselectionrequest.requestor);
fprintf(stderr, "DnD %s:%d: Selection atom = %s\n", __FILE__, __LINE__, GetAtomName(server.dsp, e.xselection.selection));
fprintf(stderr, "DnD %s:%d: Target atom = %s\n", __FILE__, __LINE__, GetAtomName(server.dsp, target));
fprintf(stderr, "DnD %s:%d: Property atom = %s\n", __FILE__, __LINE__, GetAtomName(server.dsp, e.xselection.property));
if (e.xselection.property != None && dnd_launcher_exec) {
Property prop = read_property(server.dsp, dnd_target_window, dnd_selection);
//If we're being given a list of targets (possible conversions)
if (target == server.atom.TARGETS && !dnd_sent_request) {
dnd_sent_request = 1;
dnd_atom = pick_target_from_targets(server.dsp, prop);
if (dnd_atom == None) {
fprintf(stderr, "No matching datatypes.\n");
} else { } else {
//Request the data type we are able to select cmd_length += 1; // 1 character
fprintf(stderr, "Now requsting type %s", GetAtomName(server.dsp, dnd_atom)); if (c == '`' || c == '$' || c == '\\') {
XConvertSelection(server.dsp, dnd_selection, dnd_atom, dnd_selection, dnd_target_window, CurrentTime); cmd_length += 1; // escape with one backslash
}
} else if (target == dnd_atom) {
//Dump the binary data
fprintf(stderr, "DnD %s:%d: Data begins:\n", __FILE__, __LINE__);
fprintf(stderr, "--------\n");
int i;
for (i = 0; i < prop.nitems * prop.format/8; i++)
fprintf(stderr, "%c", ((char*)prop.data)[i]);
fprintf(stderr, "--------\n");
int cmd_length = 0;
cmd_length += 1; // (
cmd_length += strlen(dnd_launcher_exec) + 1; // exec + space
cmd_length += 1; // open double quotes
for (i = 0; i < prop.nitems * prop.format/8; i++) {
char c = ((char*)prop.data)[i];
if (c == '\n') {
if (i < prop.nitems * prop.format/8 - 1) {
cmd_length += 3; // close double quotes, space, open double quotes
}
} else if (c == '\r') {
} else {
cmd_length += 1; // 1 character
if (c == '`' || c == '$' || c == '\\') {
cmd_length += 1; // escape with one backslash
}
} }
} }
cmd_length += 1; // close double quotes
cmd_length += 2; // &)
cmd_length += 1; // terminator
char *cmd = calloc(cmd_length, 1);
cmd[0] = '\0';
strcat(cmd, "(");
strcat(cmd, dnd_launcher_exec);
strcat(cmd, " \"");
for (i = 0; i < prop.nitems * prop.format/8; i++) {
char c = ((char*)prop.data)[i];
if (c == '\n') {
if (i < prop.nitems * prop.format/8 - 1) {
strcat(cmd, "\" \"");
}
} else if (c == '\r') {
} else {
if (c == '`' || c == '$' || c == '\\') {
strcat(cmd, "\\");
}
char sc[2];
sc[0] = c;
sc[1] = '\0';
strcat(cmd, sc);
}
}
strcat(cmd, "\"");
strcat(cmd, "&)");
fprintf(stderr, "DnD %s:%d: Running command: %s\n", __FILE__, __LINE__, cmd);
tint_exec(cmd);
free(cmd);
// Reply OK.
XClientMessageEvent m;
memset(&m, 0, sizeof(m));
m.type = ClientMessage;
m.display = server.dsp;
m.window = dnd_source_window;
m.message_type = server.atom.XdndFinished;
m.format = 32;
m.data.l[0] = dnd_target_window;
m.data.l[1] = 1;
m.data.l[2] = server.atom.XdndActionCopy; //We only ever copy.
XSendEvent(server.dsp, dnd_source_window, False, NoEventMask, (XEvent*)&m);
XSync(server.dsp, False);
} }
cmd_length += 1; // close double quotes
cmd_length += 2; // &)
cmd_length += 1; // terminator
XFree(prop.data); char *cmd = calloc(cmd_length, 1);
cmd[0] = '\0';
strcat(cmd, "(");
strcat(cmd, dnd_launcher_exec);
strcat(cmd, " \"");
for (i = 0; i < prop.nitems * prop.format/8; i++) {
char c = ((char*)prop.data)[i];
if (c == '\n') {
if (i < prop.nitems * prop.format/8 - 1) {
strcat(cmd, "\" \"");
}
} else if (c == '\r') {
} else {
if (c == '`' || c == '$' || c == '\\') {
strcat(cmd, "\\");
}
char sc[2];
sc[0] = c;
sc[1] = '\0';
strcat(cmd, sc);
}
}
strcat(cmd, "\"");
strcat(cmd, "&)");
fprintf(stderr, "DnD %s:%d: Running command: %s\n", __FILE__, __LINE__, cmd);
tint_exec(cmd);
free(cmd);
// Reply OK.
XClientMessageEvent m;
memset(&m, 0, sizeof(m));
m.type = ClientMessage;
m.display = server.dsp;
m.window = dnd_source_window;
m.message_type = server.atom.XdndFinished;
m.format = 32;
m.data.l[0] = dnd_target_window;
m.data.l[1] = 1;
m.data.l[2] = server.atom.XdndActionCopy; //We only ever copy.
XSendEvent(server.dsp, dnd_source_window, False, NoEventMask, (XEvent*)&m);
XSync(server.dsp, False);
} }
break; XFree(prop.data);
} }
default: break;
if (e.type == XDamageNotify+damage_event) { }
XDamageNotifyEvent *de = (XDamageNotifyEvent*)&e;
GSList *l; default:
for (l = systray.list_icons; l ; l = l->next) { if (e.type == XDamageNotify+damage_event) {
TrayWindow *traywin = (TrayWindow*)l->data; XDamageNotifyEvent *de = (XDamageNotifyEvent*)&e;
if (traywin->parent == de->drawable) { GSList *l;
systray_render_icon(traywin); for (l = systray.list_icons; l ; l = l->next) {
break; TrayWindow *traywin = (TrayWindow*)l->data;
} if (traywin->parent == de->drawable) {
systray_render_icon(traywin);
break;
} }
} }
} }
@ -1426,7 +1430,6 @@ start:
if (signal_pending == SIGUSR1) { if (signal_pending == SIGUSR1) {
// restart tint2 // restart tint2
// SIGUSR1 used when : user's signal, composite manager stop/start or xrandr // SIGUSR1 used when : user's signal, composite manager stop/start or xrandr
FD_CLR (x11_fd, &fdset); // not sure if needed
goto start; goto start;
} }
else { else {