Launcher: Fix drag and drop
This commit is contained in:
parent
14c3824632
commit
498b665c8a
6 changed files with 243 additions and 241 deletions
|
@ -31,10 +31,123 @@ static Atom dnd_selection;
|
||||||
static Atom dnd_atom;
|
static Atom dnd_atom;
|
||||||
static int dnd_sent_request;
|
static int dnd_sent_request;
|
||||||
static LauncherIcon *dnd_launcher_icon;
|
static LauncherIcon *dnd_launcher_icon;
|
||||||
static gboolean dnd_debug = FALSE;
|
gboolean debug_dnd = FALSE;
|
||||||
|
|
||||||
gboolean hidden_panel_shown_for_dnd;
|
gboolean hidden_panel_shown_for_dnd;
|
||||||
|
|
||||||
|
// This fetches all the data from a property
|
||||||
|
struct Property dnd_read_property(Display *disp, Window w, Atom property)
|
||||||
|
{
|
||||||
|
Atom actual_type;
|
||||||
|
int actual_format;
|
||||||
|
unsigned long nitems;
|
||||||
|
unsigned long bytes_after;
|
||||||
|
unsigned char *ret = 0;
|
||||||
|
|
||||||
|
int read_bytes = 1024;
|
||||||
|
|
||||||
|
// Keep trying to read the property until there are no
|
||||||
|
// bytes unread.
|
||||||
|
do {
|
||||||
|
if (ret != 0)
|
||||||
|
XFree(ret);
|
||||||
|
XGetWindowProperty(disp,
|
||||||
|
w,
|
||||||
|
property,
|
||||||
|
0,
|
||||||
|
read_bytes,
|
||||||
|
False,
|
||||||
|
AnyPropertyType,
|
||||||
|
&actual_type,
|
||||||
|
&actual_format,
|
||||||
|
&nitems,
|
||||||
|
&bytes_after,
|
||||||
|
&ret);
|
||||||
|
read_bytes *= 2;
|
||||||
|
} while (bytes_after != 0);
|
||||||
|
|
||||||
|
if (debug_dnd)
|
||||||
|
fprintf(stderr, "tint2: DnD %s:%d: Property:\n", __FILE__, __LINE__);
|
||||||
|
fprintf(stderr, "tint2: DnD %s:%d: Actual type: %s\n", __FILE__, __LINE__, GetAtomName(disp, actual_type));
|
||||||
|
fprintf(stderr, "tint2: DnD %s:%d: Actual format: %d\n", __FILE__, __LINE__, actual_format);
|
||||||
|
fprintf(stderr, "tint2: DnD %s:%d: Number of items: %lu\n", __FILE__, __LINE__, nitems);
|
||||||
|
|
||||||
|
Property p;
|
||||||
|
p.data = ret;
|
||||||
|
p.format = actual_format;
|
||||||
|
p.nitems = nitems;
|
||||||
|
p.type = actual_type;
|
||||||
|
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This function takes a list of targets which can be converted to (atom_list, nitems)
|
||||||
|
// and a list of acceptable targets with prioritees (datatypes). It returns the highest
|
||||||
|
// entry in datatypes which is also in atom_list: ie it finds the best match.
|
||||||
|
Atom dnd_pick_target_from_list(Display *disp, Atom *atom_list, int nitems)
|
||||||
|
{
|
||||||
|
Atom to_be_requested = None;
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < nitems; i++) {
|
||||||
|
const char *atom_name = GetAtomName(disp, atom_list[i]);
|
||||||
|
fprintf(stderr, "tint2: DnD %s:%d: Type %d = %s\n", __FILE__, __LINE__, i, atom_name);
|
||||||
|
|
||||||
|
// See if this data type is allowed and of higher priority (closer to zero)
|
||||||
|
// than the present one.
|
||||||
|
if (strcasecmp(atom_name, "STRING") == 0) {
|
||||||
|
to_be_requested = atom_list[i];
|
||||||
|
} else if (strcasecmp(atom_name, "text/uri-list") == 0 && !to_be_requested) {
|
||||||
|
to_be_requested = atom_list[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(stderr,
|
||||||
|
"tint2: DnD %s:%d: Accepting: Type %s\n",
|
||||||
|
__FILE__,
|
||||||
|
__LINE__,
|
||||||
|
GetAtomName(server.display, to_be_requested));
|
||||||
|
|
||||||
|
return to_be_requested;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finds the best target given up to three atoms provided (any can be None).
|
||||||
|
// Useful for part of the Xdnd protocol.
|
||||||
|
Atom dnd_pick_target_from_atoms(Display *disp, Atom t1, Atom t2, Atom t3)
|
||||||
|
{
|
||||||
|
Atom atoms[3];
|
||||||
|
int n = 0;
|
||||||
|
|
||||||
|
if (t1 != None)
|
||||||
|
atoms[n++] = t1;
|
||||||
|
|
||||||
|
if (t2 != None)
|
||||||
|
atoms[n++] = t2;
|
||||||
|
|
||||||
|
if (t3 != None)
|
||||||
|
atoms[n++] = t3;
|
||||||
|
|
||||||
|
return dnd_pick_target_from_list(disp, atoms, n);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finds the best target given a local copy of a property.
|
||||||
|
Atom dnd_pick_target_from_targets(Display *disp, Property p)
|
||||||
|
{
|
||||||
|
// The list of targets is a list of atoms, so it should have type XA_ATOM
|
||||||
|
// but it may have the type TARGETS instead.
|
||||||
|
|
||||||
|
if ((p.type != XA_ATOM && p.type != server.atom.TARGETS) || p.format != 32) {
|
||||||
|
// This would be really broken. Targets have to be an atom list
|
||||||
|
// and applications should support this. Nevertheless, some
|
||||||
|
// seem broken (MATLAB 7, for instance), so ask for STRING
|
||||||
|
// next instead as the lowest common denominator
|
||||||
|
return XA_STRING;
|
||||||
|
} else {
|
||||||
|
Atom *atom_list = (Atom *)p.data;
|
||||||
|
|
||||||
|
return dnd_pick_target_from_list(disp, atom_list, p.nitems);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void dnd_init()
|
void dnd_init()
|
||||||
{
|
{
|
||||||
dnd_source_window = 0;
|
dnd_source_window = 0;
|
||||||
|
@ -54,7 +167,7 @@ void handle_dnd_enter(XClientMessageEvent *e)
|
||||||
dnd_source_window = e->data.l[0];
|
dnd_source_window = e->data.l[0];
|
||||||
dnd_version = (e->data.l[1] >> 24);
|
dnd_version = (e->data.l[1] >> 24);
|
||||||
|
|
||||||
if (dnd_debug) {
|
if (debug_dnd) {
|
||||||
fprintf(stderr, "tint2: DnD %s:%d: DnDEnter\n", __FILE__, __LINE__);
|
fprintf(stderr, "tint2: DnD %s:%d: DnDEnter\n", __FILE__, __LINE__);
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
"DnD %s:%d: DnDEnter. Supports > 3 types = %s\n",
|
"DnD %s:%d: DnDEnter. Supports > 3 types = %s\n",
|
||||||
|
@ -83,15 +196,15 @@ void handle_dnd_enter(XClientMessageEvent *e)
|
||||||
if (more_than_3) {
|
if (more_than_3) {
|
||||||
// Fetch the list of possible conversions
|
// Fetch the list of possible conversions
|
||||||
// Notice the similarity to TARGETS with paste.
|
// Notice the similarity to TARGETS with paste.
|
||||||
Property p = read_property(server.display, dnd_source_window, server.atom.XdndTypeList);
|
Property p = dnd_read_property(server.display, dnd_source_window, server.atom.XdndTypeList);
|
||||||
dnd_atom = pick_target_from_targets(server.display, p);
|
dnd_atom = dnd_pick_target_from_targets(server.display, p);
|
||||||
XFree(p.data);
|
XFree(p.data);
|
||||||
} else {
|
} else {
|
||||||
// Use the available list
|
// Use the available list
|
||||||
dnd_atom = pick_target_from_atoms(server.display, e->data.l[2], e->data.l[3], e->data.l[4]);
|
dnd_atom = dnd_pick_target_from_atoms(server.display, e->data.l[2], e->data.l[3], e->data.l[4]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dnd_debug)
|
if (debug_dnd)
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
"tint2: DnD %s:%d: Requested type = %s\n",
|
"tint2: DnD %s:%d: Requested type = %s\n",
|
||||||
__FILE__,
|
__FILE__,
|
||||||
|
@ -135,11 +248,18 @@ void handle_dnd_position(XClientMessageEvent *e)
|
||||||
se.data.l[2] = 0; // Rectangle x,y for which no more XdndPosition events
|
se.data.l[2] = 0; // Rectangle x,y for which no more XdndPosition events
|
||||||
se.data.l[3] = (1 << 16) | 1; // Rectangle w,h for which no more XdndPosition events
|
se.data.l[3] = (1 << 16) | 1; // Rectangle w,h for which no more XdndPosition events
|
||||||
if (accept) {
|
if (accept) {
|
||||||
se.data.l[4] = dnd_version >= 2 ? e->data.l[4] : server.atom.XdndActionCopy;
|
se.data.l[4] = server.atom.XdndActionCopy;
|
||||||
} else {
|
} else {
|
||||||
se.data.l[4] = None; // None = drop will not be accepted
|
se.data.l[4] = None; // None = drop will not be accepted
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (debug_dnd)
|
||||||
|
fprintf(stderr,
|
||||||
|
"tint2: DnD %s:%d: Accepted: %s\n",
|
||||||
|
__FILE__,
|
||||||
|
__LINE__,
|
||||||
|
accept ? GetAtomName(server.display, (Atom)se.data.l[4]) : "no");
|
||||||
|
|
||||||
XSendEvent(server.display, e->data.l[0], False, NoEventMask, (XEvent *)&se);
|
XSendEvent(server.display, e->data.l[0], False, NoEventMask, (XEvent *)&se);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -149,14 +269,14 @@ void handle_dnd_drop(XClientMessageEvent *e)
|
||||||
if (dnd_version >= 1) {
|
if (dnd_version >= 1) {
|
||||||
XConvertSelection(server.display,
|
XConvertSelection(server.display,
|
||||||
server.atom.XdndSelection,
|
server.atom.XdndSelection,
|
||||||
XA_STRING,
|
dnd_atom,
|
||||||
dnd_selection,
|
dnd_selection,
|
||||||
dnd_target_window,
|
dnd_target_window,
|
||||||
e->data.l[2]);
|
e->data.l[2]);
|
||||||
} else {
|
} else {
|
||||||
XConvertSelection(server.display,
|
XConvertSelection(server.display,
|
||||||
server.atom.XdndSelection,
|
server.atom.XdndSelection,
|
||||||
XA_STRING,
|
dnd_atom,
|
||||||
dnd_selection,
|
dnd_selection,
|
||||||
dnd_target_window,
|
dnd_target_window,
|
||||||
CurrentTime);
|
CurrentTime);
|
||||||
|
@ -178,11 +298,28 @@ void handle_dnd_drop(XClientMessageEvent *e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GString *tint2_g_string_replace(GString *s, const char *from, const char *to)
|
||||||
|
{
|
||||||
|
GString *result = g_string_new("");
|
||||||
|
for (char *p = s->str; *p;) {
|
||||||
|
if (strstr(p, from) == p) {
|
||||||
|
g_string_append(result, to);
|
||||||
|
p += strlen(from);
|
||||||
|
} else {
|
||||||
|
g_string_append_c(result, *p);
|
||||||
|
p += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
g_string_assign(s, result->str);
|
||||||
|
g_string_free(result, TRUE);
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
void handle_dnd_selection_notify(XSelectionEvent *e)
|
void handle_dnd_selection_notify(XSelectionEvent *e)
|
||||||
{
|
{
|
||||||
Atom target = e->target;
|
Atom target = e->target;
|
||||||
|
|
||||||
if (dnd_debug) {
|
if (debug_dnd) {
|
||||||
fprintf(stderr, "tint2: DnD %s:%d: A selection notify has arrived!\n", __FILE__, __LINE__);
|
fprintf(stderr, "tint2: DnD %s:%d: A selection notify has arrived!\n", __FILE__, __LINE__);
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
"DnD %s:%d: Selection atom = %s\n",
|
"DnD %s:%d: Selection atom = %s\n",
|
||||||
|
@ -190,7 +327,7 @@ void handle_dnd_selection_notify(XSelectionEvent *e)
|
||||||
__LINE__,
|
__LINE__,
|
||||||
GetAtomName(server.display, e->selection));
|
GetAtomName(server.display, e->selection));
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
"tint2: DnD %s:%d: Target atom = %s\n",
|
"tint2: DnD %s:%d: Target atom = %s\n",
|
||||||
__FILE__,
|
__FILE__,
|
||||||
__LINE__,
|
__LINE__,
|
||||||
GetAtomName(server.display, target));
|
GetAtomName(server.display, target));
|
||||||
|
@ -201,116 +338,102 @@ void handle_dnd_selection_notify(XSelectionEvent *e)
|
||||||
GetAtomName(server.display, e->property));
|
GetAtomName(server.display, e->property));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (e->property != None && dnd_launcher_icon) {
|
if (dnd_launcher_icon) {
|
||||||
Property prop = read_property(server.display, dnd_target_window, dnd_selection);
|
Property prop = dnd_read_property(server.display, dnd_target_window, dnd_selection);
|
||||||
|
|
||||||
// If we're being given a list of targets (possible conversions)
|
if (prop.data) {
|
||||||
if (target == server.atom.TARGETS && !dnd_sent_request) {
|
// If we're being given a list of targets (possible conversions)
|
||||||
dnd_sent_request = 1;
|
if (target == server.atom.TARGETS && !dnd_sent_request) {
|
||||||
dnd_atom = pick_target_from_targets(server.display, prop);
|
dnd_sent_request = 1;
|
||||||
|
dnd_atom = dnd_pick_target_from_targets(server.display, prop);
|
||||||
|
|
||||||
if (dnd_atom == None) {
|
if (dnd_atom == None) {
|
||||||
if (dnd_debug)
|
if (debug_dnd)
|
||||||
fprintf(stderr, "tint2: No matching datatypes.\n");
|
fprintf(stderr, "tint2: No matching datatypes.\n");
|
||||||
} else {
|
|
||||||
// Request the data type we are able to select
|
|
||||||
if (dnd_debug)
|
|
||||||
fprintf(stderr, "tint2: Now requsting type %s", GetAtomName(server.display, dnd_atom));
|
|
||||||
XConvertSelection(server.display,
|
|
||||||
dnd_selection,
|
|
||||||
dnd_atom,
|
|
||||||
dnd_selection,
|
|
||||||
dnd_target_window,
|
|
||||||
CurrentTime);
|
|
||||||
}
|
|
||||||
} else if (target == dnd_atom) {
|
|
||||||
// Dump the binary data
|
|
||||||
if (dnd_debug) {
|
|
||||||
fprintf(stderr, "tint2: DnD %s:%d: Data begins:\n", __FILE__, __LINE__);
|
|
||||||
fprintf(stderr, "tint2: --------\n");
|
|
||||||
for (int i = 0; i < prop.nitems * prop.format / 8; i++)
|
|
||||||
fprintf(stderr, "tint2: %c", ((char *)prop.data)[i]);
|
|
||||||
fprintf(stderr, "tint2: --------\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO replace this with exec*(...)
|
|
||||||
|
|
||||||
int cmd_length = 0;
|
|
||||||
cmd_length += 1; // (
|
|
||||||
cmd_length += strlen(dnd_launcher_icon->cmd) + 1; // exec + space
|
|
||||||
cmd_length += 1; // open double quotes
|
|
||||||
for (int 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') {
|
|
||||||
// Nothing to do
|
|
||||||
} else {
|
} else {
|
||||||
cmd_length += 1; // 1 character
|
// Request the data type we are able to select
|
||||||
if (c == '`' || c == '$' || c == '\\') {
|
if (debug_dnd)
|
||||||
cmd_length += 1; // escape with one backslash
|
fprintf(stderr, "tint2: Now requsting type %s", GetAtomName(server.display, dnd_atom));
|
||||||
}
|
XConvertSelection(server.display,
|
||||||
|
dnd_selection,
|
||||||
|
dnd_atom,
|
||||||
|
dnd_selection,
|
||||||
|
dnd_target_window,
|
||||||
|
CurrentTime);
|
||||||
}
|
}
|
||||||
}
|
} else if (target == dnd_atom) {
|
||||||
cmd_length += 1; // close double quotes
|
// Dump the binary data
|
||||||
cmd_length += 2; // &)
|
if (debug_dnd) {
|
||||||
cmd_length += 1; // terminator
|
fprintf(stderr, "tint2: DnD %s:%d: Received data:\n", __FILE__, __LINE__);
|
||||||
|
fprintf(stderr, "tint2: --------\n");
|
||||||
char *cmd = (char *)calloc(cmd_length, 1);
|
for (int i = 0; i < prop.nitems * prop.format / 8; i++)
|
||||||
cmd[0] = '\0';
|
fprintf(stderr, "%c", ((char *)prop.data)[i]);
|
||||||
strcat(cmd, "(");
|
fprintf(stderr, "tint2: --------\n");
|
||||||
strcat(cmd, dnd_launcher_icon->cmd);
|
|
||||||
strcat(cmd, " \"");
|
|
||||||
for (int 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') {
|
|
||||||
// Nothing to do
|
|
||||||
} else {
|
|
||||||
if (c == '`' || c == '$' || c == '\\') {
|
|
||||||
strcat(cmd, "\\");
|
|
||||||
}
|
|
||||||
char sc[2];
|
|
||||||
sc[0] = c;
|
|
||||||
sc[1] = '\0';
|
|
||||||
strcat(cmd, sc);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
strcat(cmd, "\"");
|
|
||||||
strcat(cmd, "&)");
|
|
||||||
if (dnd_debug)
|
|
||||||
fprintf(stderr, "tint2: DnD %s:%d: Running command: %s\n", __FILE__, __LINE__, cmd);
|
|
||||||
tint_exec(cmd,
|
|
||||||
NULL,
|
|
||||||
NULL,
|
|
||||||
e->time,
|
|
||||||
NULL,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
dnd_launcher_icon->start_in_terminal,
|
|
||||||
dnd_launcher_icon->startup_notification);
|
|
||||||
free(cmd);
|
|
||||||
|
|
||||||
// Reply OK.
|
// TODO: support %r nd %F
|
||||||
XClientMessageEvent m;
|
// https://standards.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html#exec-variables
|
||||||
memset(&m, 0, sizeof(m));
|
GString *cmd = g_string_new(dnd_launcher_icon->cmd);
|
||||||
m.type = ClientMessage;
|
|
||||||
m.display = server.display;
|
const char *atom_name = GetAtomName(server.display, prop.type);
|
||||||
m.window = dnd_source_window;
|
if (strcasecmp(atom_name, "STRING") == 0 ||
|
||||||
m.message_type = server.atom.XdndFinished;
|
strcasecmp(atom_name, "text/uri-list") == 0) {
|
||||||
m.format = 32;
|
GString *url = g_string_new("");
|
||||||
m.data.l[0] = dnd_target_window;
|
GString *prev_url = g_string_new("");
|
||||||
m.data.l[1] = 1;
|
for (int i = 0; i < prop.nitems * prop.format / 8; i++) {
|
||||||
m.data.l[2] = server.atom.XdndActionCopy; // We only ever copy.
|
char c = ((char *)prop.data)[i];
|
||||||
XSendEvent(server.display, dnd_source_window, False, NoEventMask, (XEvent *)&m);
|
if (c == '\n') {
|
||||||
XSync(server.display, False);
|
// Many programs cannot handle this prefix
|
||||||
|
tint2_g_string_replace(url, "file://", "");
|
||||||
|
// Some programs put duplicates in the list, we remove them
|
||||||
|
if (strcmp(url->str, prev_url->str) != 0) {
|
||||||
|
g_string_append(cmd, " \"");
|
||||||
|
g_string_append(cmd, url->str);
|
||||||
|
g_string_append(cmd, "\"");
|
||||||
|
}
|
||||||
|
g_string_assign(prev_url, url->str);
|
||||||
|
g_string_assign(url, "");
|
||||||
|
} else if (c == '\r') {
|
||||||
|
// Nothing to do
|
||||||
|
} else {
|
||||||
|
if (c == '`' || c == '$' || c == '\\') {
|
||||||
|
g_string_append(url, "\\");
|
||||||
|
}
|
||||||
|
g_string_append_c(url, c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
g_string_free(url, TRUE);
|
||||||
|
g_string_free(prev_url, TRUE);
|
||||||
|
}
|
||||||
|
if (debug_dnd)
|
||||||
|
fprintf(stderr, "tint2: DnD %s:%d: Running command: %s\n", __FILE__, __LINE__, cmd->str);
|
||||||
|
tint_exec(cmd->str,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
e->time,
|
||||||
|
NULL,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
dnd_launcher_icon->start_in_terminal,
|
||||||
|
dnd_launcher_icon->startup_notification);
|
||||||
|
g_string_free(cmd, TRUE);
|
||||||
|
|
||||||
|
// Reply OK.
|
||||||
|
XClientMessageEvent m;
|
||||||
|
memset(&m, 0, sizeof(m));
|
||||||
|
m.type = ClientMessage;
|
||||||
|
m.display = server.display;
|
||||||
|
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.display, dnd_source_window, False, NoEventMask, (XEvent *)&m);
|
||||||
|
XSync(server.display, False);
|
||||||
|
}
|
||||||
|
|
||||||
|
XFree(prop.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
XFree(prop.data);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
#include <glib.h>
|
#include <glib.h>
|
||||||
|
|
||||||
extern gboolean hidden_panel_shown_for_dnd;
|
extern gboolean hidden_panel_shown_for_dnd;
|
||||||
|
extern gboolean debug_dnd;
|
||||||
|
|
||||||
void dnd_init();
|
void dnd_init();
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
#include "drag_and_drop.h"
|
||||||
#include "fps_distribution.h"
|
#include "fps_distribution.h"
|
||||||
#include "panel.h"
|
#include "panel.h"
|
||||||
#include "server.h"
|
#include "server.h"
|
||||||
|
@ -86,6 +87,7 @@ void handle_env_vars()
|
||||||
debug_icons = getenv("DEBUG_ICONS") != NULL;
|
debug_icons = getenv("DEBUG_ICONS") != NULL;
|
||||||
debug_fps = getenv("DEBUG_FPS") != NULL;
|
debug_fps = getenv("DEBUG_FPS") != NULL;
|
||||||
debug_frames = getenv("DEBUG_FRAMES") != NULL;
|
debug_frames = getenv("DEBUG_FRAMES") != NULL;
|
||||||
|
debug_dnd = getenv("DEBUG_DND") != NULL;
|
||||||
if (debug_fps) {
|
if (debug_fps) {
|
||||||
init_fps_distribution();
|
init_fps_distribution();
|
||||||
char *s = getenv("TRACING_FPS_THRESHOLD");
|
char *s = getenv("TRACING_FPS_THRESHOLD");
|
||||||
|
|
104
src/server.c
104
src/server.c
|
@ -131,110 +131,6 @@ const char *GetAtomName(Display *disp, Atom a)
|
||||||
return XGetAtomName(disp, a);
|
return XGetAtomName(disp, a);
|
||||||
}
|
}
|
||||||
|
|
||||||
// This fetches all the data from a property
|
|
||||||
struct Property read_property(Display *disp, Window w, Atom property)
|
|
||||||
{
|
|
||||||
Atom actual_type;
|
|
||||||
int actual_format;
|
|
||||||
unsigned long nitems;
|
|
||||||
unsigned long bytes_after;
|
|
||||||
unsigned char *ret = 0;
|
|
||||||
|
|
||||||
int read_bytes = 1024;
|
|
||||||
|
|
||||||
// Keep trying to read the property until there are no
|
|
||||||
// bytes unread.
|
|
||||||
do {
|
|
||||||
if (ret != 0)
|
|
||||||
XFree(ret);
|
|
||||||
XGetWindowProperty(disp,
|
|
||||||
w,
|
|
||||||
property,
|
|
||||||
0,
|
|
||||||
read_bytes,
|
|
||||||
False,
|
|
||||||
AnyPropertyType,
|
|
||||||
&actual_type,
|
|
||||||
&actual_format,
|
|
||||||
&nitems,
|
|
||||||
&bytes_after,
|
|
||||||
&ret);
|
|
||||||
read_bytes *= 2;
|
|
||||||
} while (bytes_after != 0);
|
|
||||||
|
|
||||||
fprintf(stderr, "tint2: DnD %s:%d: Property:\n", __FILE__, __LINE__);
|
|
||||||
fprintf(stderr, "tint2: DnD %s:%d: Actual type: %s\n", __FILE__, __LINE__, GetAtomName(disp, actual_type));
|
|
||||||
fprintf(stderr, "tint2: DnD %s:%d: Actual format: %d\n", __FILE__, __LINE__, actual_format);
|
|
||||||
fprintf(stderr, "tint2: DnD %s:%d: Number of items: %lu\n", __FILE__, __LINE__, nitems);
|
|
||||||
|
|
||||||
Property p;
|
|
||||||
p.data = ret;
|
|
||||||
p.format = actual_format;
|
|
||||||
p.nitems = nitems;
|
|
||||||
p.type = actual_type;
|
|
||||||
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
|
|
||||||
// This function takes a list of targets which can be converted to (atom_list, nitems)
|
|
||||||
// and a list of acceptable targets with prioritees (datatypes). It returns the highest
|
|
||||||
// entry in datatypes which is also in atom_list: ie it finds the best match.
|
|
||||||
Atom pick_target_from_list(Display *disp, Atom *atom_list, int nitems)
|
|
||||||
{
|
|
||||||
Atom to_be_requested = None;
|
|
||||||
int i;
|
|
||||||
for (i = 0; i < nitems; i++) {
|
|
||||||
const char *atom_name = GetAtomName(disp, atom_list[i]);
|
|
||||||
fprintf(stderr, "tint2: DnD %s:%d: Type %d = %s\n", __FILE__, __LINE__, i, atom_name);
|
|
||||||
|
|
||||||
// See if this data type is allowed and of higher priority (closer to zero)
|
|
||||||
// than the present one.
|
|
||||||
if (strcmp(atom_name, "STRING") == 0) {
|
|
||||||
to_be_requested = atom_list[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return to_be_requested;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Finds the best target given up to three atoms provided (any can be None).
|
|
||||||
// Useful for part of the Xdnd protocol.
|
|
||||||
Atom pick_target_from_atoms(Display *disp, Atom t1, Atom t2, Atom t3)
|
|
||||||
{
|
|
||||||
Atom atoms[3];
|
|
||||||
int n = 0;
|
|
||||||
|
|
||||||
if (t1 != None)
|
|
||||||
atoms[n++] = t1;
|
|
||||||
|
|
||||||
if (t2 != None)
|
|
||||||
atoms[n++] = t2;
|
|
||||||
|
|
||||||
if (t3 != None)
|
|
||||||
atoms[n++] = t3;
|
|
||||||
|
|
||||||
return pick_target_from_list(disp, atoms, n);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Finds the best target given a local copy of a property.
|
|
||||||
Atom pick_target_from_targets(Display *disp, Property p)
|
|
||||||
{
|
|
||||||
// The list of targets is a list of atoms, so it should have type XA_ATOM
|
|
||||||
// but it may have the type TARGETS instead.
|
|
||||||
|
|
||||||
if ((p.type != XA_ATOM && p.type != server.atom.TARGETS) || p.format != 32) {
|
|
||||||
// This would be really broken. Targets have to be an atom list
|
|
||||||
// and applications should support this. Nevertheless, some
|
|
||||||
// seem broken (MATLAB 7, for instance), so ask for STRING
|
|
||||||
// next instead as the lowest common denominator
|
|
||||||
return XA_STRING;
|
|
||||||
} else {
|
|
||||||
Atom *atom_list = (Atom *)p.data;
|
|
||||||
|
|
||||||
return pick_target_from_list(disp, atom_list, p.nitems);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void cleanup_server()
|
void cleanup_server()
|
||||||
{
|
{
|
||||||
if (server.colormap)
|
if (server.colormap)
|
||||||
|
|
15
src/server.h
15
src/server.h
|
@ -102,21 +102,6 @@ typedef struct Property {
|
||||||
// Returns the name of an Atom as string. Do not free the string.
|
// Returns the name of an Atom as string. Do not free the string.
|
||||||
const char *GetAtomName(Display *disp, Atom a);
|
const char *GetAtomName(Display *disp, Atom a);
|
||||||
|
|
||||||
// This function takes a list of targets which can be converted to (atom_list, nitems)
|
|
||||||
// and a list of acceptable targets with prioritees (datatypes). It returns the highest
|
|
||||||
// entry in datatypes which is also in atom_list: ie it finds the best match.
|
|
||||||
Atom pick_target_from_list(Display *disp, Atom *atom_list, int nitems);
|
|
||||||
|
|
||||||
// Finds the best target given up to three atoms provided (any can be None).
|
|
||||||
// Useful for part of the Xdnd protocol.
|
|
||||||
Atom pick_target_from_atoms(Display *disp, Atom t1, Atom t2, Atom t3);
|
|
||||||
|
|
||||||
// Finds the best target given a local copy of a property.
|
|
||||||
Atom pick_target_from_targets(Display *disp, Property p);
|
|
||||||
|
|
||||||
// This fetches all the data from a property
|
|
||||||
struct Property read_property(Display *disp, Window w, Atom property);
|
|
||||||
|
|
||||||
typedef struct Monitor {
|
typedef struct Monitor {
|
||||||
int x;
|
int x;
|
||||||
int y;
|
int y;
|
||||||
|
|
|
@ -404,14 +404,9 @@ pid_t tint_exec(const char *command,
|
||||||
words.we_wordv[0] = (char*)"x-terminal-emulator";
|
words.we_wordv[0] = (char*)"x-terminal-emulator";
|
||||||
words.we_wordv[1] = (char*)"-e";
|
words.we_wordv[1] = (char*)"-e";
|
||||||
execvp("x-terminal-emulator", words.we_wordv);
|
execvp("x-terminal-emulator", words.we_wordv);
|
||||||
|
fprintf(stderr, "tint2: could not execute command in x-terminal-emulator: %s, executting in shell\n", command);
|
||||||
}
|
}
|
||||||
fprintf(stderr, "tint2: could not execute command in x-terminal-emulator: %s, executting in shell\n", command);
|
execlp("sh", "sh", "-c", command, NULL);
|
||||||
wordexp_t words;
|
|
||||||
words.we_offs = 2;
|
|
||||||
wordexp(command, &words, WRDE_DOOFFS | WRDE_SHOWERR);
|
|
||||||
words.we_wordv[0] = (char*)"sh";
|
|
||||||
words.we_wordv[1] = (char*)"-c";
|
|
||||||
execvp("sh", words.we_wordv);
|
|
||||||
fprintf(stderr, "tint2: Failed to execute %s\n", command);
|
fprintf(stderr, "tint2: Failed to execute %s\n", command);
|
||||||
#if HAVE_SN
|
#if HAVE_SN
|
||||||
if (startup_notifications && startup_notification && time) {
|
if (startup_notifications && startup_notification && time) {
|
||||||
|
|
Loading…
Reference in a new issue