spacepaste

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
// defined so that we get the stable ABI from 3.2 onwards - means we don't have to 
// recompile this module for every new python version.
#define Py_LIMITED_API
// has to be included first
#include "Python.h"
#include <Windows.h>

struct Modifier {
	WORD vk;
	bool set;
};

static void sendCtrlV() {
	INPUT ctrlV[] = {{0},{0},{0},{0}};
	ctrlV[0].type = INPUT_KEYBOARD;
	ctrlV[0].ki.wVk = VK_LCONTROL;

	ctrlV[1].type = INPUT_KEYBOARD;
	ctrlV[1].ki.wVk = 'V';

	ctrlV[2].type = INPUT_KEYBOARD;
	ctrlV[2].ki.wVk = 'V';
	ctrlV[2].ki.dwFlags = KEYEVENTF_KEYUP;

	ctrlV[3].type = INPUT_KEYBOARD;
	ctrlV[3].ki.wVk = VK_LCONTROL;
	ctrlV[3].ki.dwFlags = KEYEVENTF_KEYUP;

	SendInput(sizeof(ctrlV)/sizeof(INPUT), ctrlV, sizeof(INPUT));
}

static void sendEvent(WORD vk, DWORD flags) {
	INPUT in = {0};
	in.type = INPUT_KEYBOARD;
	in.ki.wVk = vk;
	in.ki.dwFlags = flags;
	SendInput(1, &in, sizeof(INPUT));
}

/*
	Works as following: First record which control keys are down, make sure they are
	up, then send the ctrl-v command, then reinstate original state.
	If we don't do this we may get interesting results, i.e.: Ctrl+Alt+V instead of Ctrl-v won't
	do what we want.
*/
static PyObject* sendPasteCommand(PyObject *self, PyObject *args) {
	Modifier modifiers[] = {
		{VK_MENU, 0}, /* alt key*/
		{VK_CONTROL, 0},
		{VK_SHIFT, 0},
		{VK_LWIN, 0},
		{VK_RWIN, 0},
	};
	for (int i = 0; i < sizeof(modifiers)/sizeof(Modifier); i++) {
		Modifier& mod = modifiers[i];
		if (GetAsyncKeyState(mod.vk) < 0) {
			mod.set = true;
			sendEvent(mod.vk, KEYEVENTF_KEYUP);
		}
	}
	sendCtrlV();
	for (int i = 0; i < sizeof(modifiers)/sizeof(Modifier); i++) {
		Modifier& mod = modifiers[i];
		if (mod.set) sendEvent(mod.vk, 0 /* key down */);
	}
	Py_RETURN_NONE;
}


/*
	This only works correctly when the shortcut itself is something like ctrl-<something>,
	but is faster, simpler and should work better in such situations. 
*/
static PyObject* sendV(PyObject *self, PyObject *args) {
	INPUT inputs[] = {{0}, {0}};
	inputs[0].type = INPUT_KEYBOARD;
	inputs[0].ki.wVk = 'V';
	inputs[1].type = INPUT_KEYBOARD;
	inputs[1].ki.wVk = 'V';
	inputs[1].ki.dwFlags = KEYEVENTF_KEYUP;
	SendInput(sizeof(inputs) / sizeof(INPUT), inputs, sizeof(INPUT));
	Py_RETURN_NONE;
}

static PyMethodDef methods[] = { /* see http://docs.python.org/py3k/c-api/structures.html#PyMethodDef */
    {"send_paste",		/* name of python function in module */
	sendPasteCommand,	/* pointer to c function */
	METH_VARARGS,		/* parameters are provided as tuple, if METH_VARARGS | METH_KEYWORDS is
							specified as a third parameter a dictionary of keywords is passed */
	"sends ctrl-v to current application" },
    {"send_paste_simple",	
	sendV,
	METH_VARARGS,
	"sends baiscally only a 'v' to the application. This is much simpler and faster "
	"than the complete version (so should work better), but also only works if the shortcut "
	"is only ctrl-something"
	},
    {NULL, NULL, 0, NULL} /* guard */
};

static struct PyModuleDef sendpastemodule = {
    PyModuleDef_HEAD_INIT,	
    "sendpaste",	/* name of module */
    "",		/* doc string */
    -1,		/* size of per-interpreter state of the module,
				or -1 if the module keeps state in global variables. 
				state can be accessed by PyModule_GetState() */
    methods,
    NULL,	/* m_reload: unused, should be null */	
    NULL,	/* m_traverse: A traversal function to call during GC traversal of the module object, 
				or NULL if not needed. */
    NULL,	/* m_clear: A clear function to call during GC clearing of the module object, 
				or NULL if not needed. */
    NULL	/* m_free: A function to call during deallocation of the module object, 
				or NULL if not needed. */
};

PyMODINIT_FUNC PyInit_sendpaste() {
	return PyModule_Create(&sendpastemodule);
}