Compare commits
10 Commits
8f8e68a83b
...
bf72370337
Author | SHA1 | Date | |
---|---|---|---|
|
bf72370337 | ||
|
e25dbbc11f | ||
|
a45b0cae85 | ||
|
2f8199586d | ||
|
2264356f29 | ||
|
1190c30604 | ||
|
dbaf9f0223 | ||
|
e1dbccf7f6 | ||
|
25a68d31cc | ||
|
4405557a32 |
10
Makefile
10
Makefile
@ -16,8 +16,10 @@
|
|||||||
|
|
||||||
CC=gcc
|
CC=gcc
|
||||||
DEBUG=0
|
DEBUG=0
|
||||||
|
PREFIX=/usr/local
|
||||||
INCFLAGS=`pkg-config x11 --cflags`
|
INCFLAGS=`pkg-config x11 --cflags`
|
||||||
LDFLAGS=`pkg-config x11 --libs`
|
LDFLAGS=`pkg-config x11 --libs`
|
||||||
|
DEFS=-DVERSION=\"1.0\"
|
||||||
CFLAGS=$(INCFLAGS) -std=c99 -Wall -Wextra -Wfatal-errors -Werror
|
CFLAGS=$(INCFLAGS) -std=c99 -Wall -Wextra -Wfatal-errors -Werror
|
||||||
HDRS=
|
HDRS=
|
||||||
OBJS=src/main.o
|
OBJS=src/main.o
|
||||||
@ -34,7 +36,13 @@ endif
|
|||||||
tinyclip: $(OBJS)
|
tinyclip: $(OBJS)
|
||||||
$(CC) -o $@ $^ $(LDFLAGS)
|
$(CC) -o $@ $^ $(LDFLAGS)
|
||||||
|
|
||||||
.PHONY: clean
|
.PHONY: clean distclean install
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
$(RM) $(OBJS)
|
$(RM) $(OBJS)
|
||||||
|
|
||||||
|
distclean: clean
|
||||||
|
$(RM) tinyclip
|
||||||
|
|
||||||
|
install: tinyclip
|
||||||
|
install -m 755 tinyclip $(PREFIX)/bin/
|
||||||
|
19
README
19
README
@ -3,6 +3,25 @@
|
|||||||
================
|
================
|
||||||
A tiny clipboard application for X.
|
A tiny clipboard application for X.
|
||||||
|
|
||||||
|
# Building
|
||||||
|
----------
|
||||||
|
## Dependencies
|
||||||
|
To compile the project you will require:
|
||||||
|
* C99 compatible compiler (GCC is recommended)
|
||||||
|
* UNIX Make
|
||||||
|
* X11
|
||||||
|
|
||||||
|
## Compiling
|
||||||
|
The project is built using the `make' command. For debug information
|
||||||
|
append `DEBUG=1' to the command.
|
||||||
|
|
||||||
|
# Installation
|
||||||
|
--------------
|
||||||
|
Once the project is built, you can install by running `make install'. By
|
||||||
|
default the `PREFIX' variable is set to `/usr/local' (installing the
|
||||||
|
binary in `/usr/local/bin'). You can override this by appending
|
||||||
|
`PREFIX=<prefix>' to the install command.
|
||||||
|
|
||||||
# License
|
# License
|
||||||
---------
|
---------
|
||||||
This project is licensed under the GNU General Public License version 3
|
This project is licensed under the GNU General Public License version 3
|
||||||
|
5
TODO
Normal file
5
TODO
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
============
|
||||||
|
*** TODO ***
|
||||||
|
============
|
||||||
|
- Setup response for INCR
|
||||||
|
- Implement most common plain text targets
|
138
src/main.c
138
src/main.c
@ -19,41 +19,114 @@
|
|||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
#include <X11/Xlib.h>
|
#include <X11/Xlib.h>
|
||||||
|
|
||||||
|
Display *display;
|
||||||
|
Window clip_win;
|
||||||
int run;
|
int run;
|
||||||
void *data;
|
void *data;
|
||||||
size_t data_size;
|
size_t data_size;
|
||||||
|
|
||||||
void quit() {
|
void quit() {
|
||||||
run = 0;
|
run = 0;
|
||||||
|
XClientMessageEvent cme;
|
||||||
|
cme.type = ClientMessage;
|
||||||
|
cme.format = 32;
|
||||||
|
XSendEvent(display, clip_win, 0, 0, (XEvent*)&cme);
|
||||||
|
XFlush(display);
|
||||||
}
|
}
|
||||||
|
|
||||||
void send_selection(Display *display, XSelectionRequestEvent *sre, Atom utf8) {
|
void get_selection(Atom property) {
|
||||||
XSelectionEvent se;
|
Atom da, incr, type;
|
||||||
time_t now_tm;
|
int di;
|
||||||
char *now;
|
unsigned long size, dul;
|
||||||
|
unsigned char *prop_ret = NULL;
|
||||||
|
|
||||||
now_tm = time(NULL);
|
XGetWindowProperty(display, clip_win, property, 0, 0, 0, AnyPropertyType,
|
||||||
now = ctime(&now_tm);
|
&type, &di, &dul, &size, &prop_ret);
|
||||||
|
XFree(prop_ret);
|
||||||
|
|
||||||
|
incr = XInternAtom(display, "INCR", 0);
|
||||||
|
if(type == incr)
|
||||||
|
{
|
||||||
|
// TODO: prepare for multiple messages
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
char *an = XGetAtomName(display, sre->property);
|
printf("Property size: %lu\n", size);
|
||||||
printf("Sending CLIPBOARD to window 0x%1x, property '%s'\n", sre->requestor, an);
|
#endif
|
||||||
if(an)
|
XGetWindowProperty(display, clip_win, property, 0, size, 0, AnyPropertyType,
|
||||||
XFree(an);
|
&da, &di, &dul, &dul, &prop_ret);
|
||||||
|
free(data);
|
||||||
|
data = malloc(size);
|
||||||
|
strcpy(data, (void*)prop_ret);
|
||||||
|
data_size = size;
|
||||||
|
#ifdef DEBUG
|
||||||
|
printf("Received data: %s\n", (char*)data);
|
||||||
|
#endif
|
||||||
|
XFree(prop_ret);
|
||||||
|
XDeleteProperty(display, clip_win, property);
|
||||||
|
}
|
||||||
|
|
||||||
|
void send_reject(XSelectionRequestEvent *sre) {
|
||||||
|
XSelectionEvent reply_se;
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
char *target_name;
|
||||||
|
target_name = XGetAtomName(display, sre->target);
|
||||||
|
printf("Denying request for type `%s'\n", target_name);
|
||||||
|
if(target_name)
|
||||||
|
XFree(target_name);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
XChangeProperty(display, sre->requestor, sre->property, utf8, 8, PropModeReplace, data, data_size);
|
reply_se.type = SelectionNotify;
|
||||||
|
reply_se.requestor = sre->requestor;
|
||||||
|
reply_se.selection = sre->selection;
|
||||||
|
reply_se.target = sre->target;
|
||||||
|
reply_se.property = None;
|
||||||
|
reply_se.time = sre->time;
|
||||||
|
|
||||||
|
XSendEvent(display, sre->requestor, 1,
|
||||||
|
NoEventMask, (XEvent*)&reply_se);
|
||||||
|
}
|
||||||
|
|
||||||
|
void send_selection(XSelectionRequestEvent *sre, Atom target)
|
||||||
|
{
|
||||||
|
XSelectionEvent reply_se;
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
char *target_name = XGetAtomName(display, sre->property);
|
||||||
|
printf("Sending CLIPBOARD to window 0x%1x, property '%s'\n",
|
||||||
|
(unsigned int)sre->requestor, target_name);
|
||||||
|
if(target_name)
|
||||||
|
XFree(target_name);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
XChangeProperty(display, sre->requestor, sre->property,
|
||||||
|
target, 8, PropModeReplace, (unsigned char*)data, data_size);
|
||||||
|
|
||||||
|
reply_se.type = SelectionNotify;
|
||||||
|
reply_se.requestor = sre->requestor;
|
||||||
|
reply_se.selection = sre->selection;
|
||||||
|
reply_se.target = sre->target;
|
||||||
|
reply_se.property = sre->property;
|
||||||
|
reply_se.time = sre->time;
|
||||||
|
|
||||||
|
XSendEvent(display, sre->requestor, 1,
|
||||||
|
NoEventMask, (XEvent*)&reply_se);
|
||||||
}
|
}
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
Display *display;
|
Window root, owner;
|
||||||
Window clip_win, root, owner;
|
|
||||||
int screen;
|
int screen;
|
||||||
Atom selection, utf8, clip_property;
|
Atom selection, clip_property;
|
||||||
XEvent event;
|
// target atoms
|
||||||
|
Atom utf8_target;//, text_target, string_target;
|
||||||
|
data = NULL;
|
||||||
|
data_size = 0;
|
||||||
|
|
||||||
// get current X display
|
// get current X display
|
||||||
display = XOpenDisplay(NULL);
|
display = XOpenDisplay(NULL);
|
||||||
@ -68,11 +141,13 @@ int main() {
|
|||||||
// create a dummy window for tinyclip
|
// create a dummy window for tinyclip
|
||||||
clip_win = XCreateSimpleWindow(display, root, -10, -10, 1, 1, 0, 0, 0);
|
clip_win = XCreateSimpleWindow(display, root, -10, -10, 1, 1, 0, 0, 0);
|
||||||
XSelectInput(display, clip_win,
|
XSelectInput(display, clip_win,
|
||||||
SelectionClear | SelectionRequest | SelectionNotify);
|
SelectionClear | SelectionRequest | SelectionNotify | ClientMessage);
|
||||||
|
|
||||||
// create atoms for selections
|
// create atoms for selections
|
||||||
selection = XInternAtom(display, "CLIPBOARD", 0);
|
selection = XInternAtom(display, "CLIPBOARD", 0);
|
||||||
utf8 = XInternAtom(display, "UTF8_STRING", 0);
|
utf8_target = XInternAtom(display, "UTF8_STRING", 0);
|
||||||
|
//text_target = XInternAtom(display, "TEXT", 0);
|
||||||
|
//string_target = XInternAtom(display, "STRING", 0);
|
||||||
clip_property = XInternAtom(display, "TINYCLIP", 0);
|
clip_property = XInternAtom(display, "TINYCLIP", 0);
|
||||||
|
|
||||||
// check if there is currently someone using the clipboard
|
// check if there is currently someone using the clipboard
|
||||||
@ -81,9 +156,9 @@ int main() {
|
|||||||
{
|
{
|
||||||
// take ownership of clipboard while copying contents
|
// take ownership of clipboard while copying contents
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
printf("Current owner: 0x%1X\n", owner);
|
printf("Current owner: 0x%1X\n", (unsigned int)owner);
|
||||||
#endif
|
#endif
|
||||||
XConvertSelection(display, selection, utf8,
|
XConvertSelection(display, selection, utf8_target,
|
||||||
clip_property, clip_win, CurrentTime);
|
clip_property, clip_win, CurrentTime);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -94,29 +169,29 @@ int main() {
|
|||||||
run = 1;
|
run = 1;
|
||||||
signal(SIGINT, quit);
|
signal(SIGINT, quit);
|
||||||
|
|
||||||
|
Atom wmDeleteMessage = XInternAtom(display, "WM_DELETE_WINDOW", 0);
|
||||||
|
XSetWMProtocols(display, clip_win, &wmDeleteMessage, 1);
|
||||||
|
|
||||||
while(run)
|
while(run)
|
||||||
{
|
{
|
||||||
XNextEvent(display, &event);
|
XEvent event;
|
||||||
XSelectionRequestEvent *sre;
|
XSelectionRequestEvent *sre;
|
||||||
XSelectionEvent *se;
|
XSelectionEvent *se;
|
||||||
|
XNextEvent(display, &event);
|
||||||
switch(event.type)
|
switch(event.type)
|
||||||
{
|
{
|
||||||
case SelectionClear:
|
case SelectionClear:
|
||||||
// if someone else took the clipboard, regain ownership
|
// if someone else took the clipboard, regain ownership
|
||||||
#ifdef DEBUG
|
XConvertSelection(display, selection, utf8_target,
|
||||||
printf("Lost selection.\n");
|
|
||||||
#endif
|
|
||||||
XConvertSelection(display, selection, utf8,
|
|
||||||
clip_property, clip_win, CurrentTime);
|
clip_property, clip_win, CurrentTime);
|
||||||
break;
|
break;
|
||||||
case SelectionNotify:
|
case SelectionNotify:
|
||||||
|
// we've been sent the new CLIPBOARD, now we take ownership of it
|
||||||
se = (XSelectionEvent*)&event.xselection;
|
se = (XSelectionEvent*)&event.xselection;
|
||||||
if(se->property == None)
|
if(se->property == None)
|
||||||
fprintf(stderr, "Conversion impossible.");
|
fprintf(stderr, "Conversion impossible.");
|
||||||
else
|
else
|
||||||
{
|
get_selection(clip_property);
|
||||||
// TODO: handle new clipboard information
|
|
||||||
}
|
|
||||||
XSetSelectionOwner(display, selection, clip_win, CurrentTime);
|
XSetSelectionOwner(display, selection, clip_win, CurrentTime);
|
||||||
break;
|
break;
|
||||||
case SelectionRequest:
|
case SelectionRequest:
|
||||||
@ -124,9 +199,14 @@ int main() {
|
|||||||
sre = (XSelectionRequestEvent*) &event.xselectionrequest;
|
sre = (XSelectionRequestEvent*) &event.xselectionrequest;
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
printf("New selection request:\n"
|
printf("New selection request:\n"
|
||||||
" requestor: 0x%1x\n", sre->requestor);
|
" requestor: 0x%1x\n", (unsigned int)sre->requestor);
|
||||||
#endif
|
#endif
|
||||||
send_selection(display, sre, utf8);
|
if(sre->target != utf8_target || sre->property == None)
|
||||||
|
send_reject(sre);
|
||||||
|
else
|
||||||
|
send_selection(sre, utf8_target);
|
||||||
|
break;
|
||||||
|
case ClientMessage:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user