[Spice-devel] [spice-gtk 7/9] Implements set_keyboard_lock_modifiers for Windows

Frediano Ziglio fziglio at redhat.com
Wed Jun 8 12:55:28 UTC 2016


> 
> ----- Original Message -----
> > Signed-off-by: Frediano Ziglio <fziglio at redhat.com>
> > ---
> >  src/keyboard-modifiers.c | 534
> >  +++++++++++++++++++++++++++++++++++++++++++++++
> >  tests/Makefile.am        |  15 ++
> >  tests/keyboard-test.c    |   9 +
> >  3 files changed, 558 insertions(+)
> >  create mode 100644 tests/keyboard-test.c
> > 
> > diff --git a/src/keyboard-modifiers.c b/src/keyboard-modifiers.c
> > index dd13fad..cb019c9 100644
> > --- a/src/keyboard-modifiers.c
> > +++ b/src/keyboard-modifiers.c
> > @@ -17,6 +17,8 @@
> >  */
> >  #include "config.h"
> >  
> > +#include <glib.h>
> > +
> >  #ifdef HAVE_X11_XKBLIB_H
> >  #include <X11/XKBlib.h>
> >  #include <gdk/gdkx.h>
> > @@ -35,6 +37,7 @@
> >  #include "channel-inputs.h"
> >  #include "keyboard-modifiers.h"
> >  
> > +#if !KEYBOARD_MODIFIERS_TEST
> >  guint32 get_keyboard_lock_modifiers(void)
> >  {
> >      guint32 modifiers = 0;
> > @@ -89,6 +92,7 @@ guint32 get_keyboard_lock_modifiers(void)
> >  #endif // GTK_CHECK_VERSION(3,18,0)
> >      return modifiers;
> >  }
> > +#endif
> >  
> >  #if defined(HAVE_X11_XKBLIB_H)
> >  typedef enum SpiceLed {
> > @@ -158,6 +162,536 @@ void set_keyboard_lock_modifiers(guint32 modifiers)
> >      set_keyboard_led(x_display, SCROLL_LOCK_LED, !!(modifiers &
> >      SPICE_INPUTS_SCROLL_LOCK));
> >  }
> >  
> > +#elif defined(G_OS_WIN32)
> > +
> > +// Some definitions from kbd.h to define internal layout file structures
> > +// Note that pointer in Wow64 are 64 bit despite program bits
> > +#define KBDSPECIAL (USHORT)0x0400
> > +
> > +// type of NLS function key
> > +#define KBDNLS_TYPE_NULL      0
> > +#define KBDNLS_TYPE_NORMAL    1
> > +#define KBDNLS_TYPE_TOGGLE    2
> > +
> > +// action to perform on a specific combination (only needed)
> > +#define KBDNLS_NULL             0 // Invalid function
> > +#define KBDNLS_SEND_BASE_VK     2 // Send Base VK_xxx
> > +#define KBDNLS_SEND_PARAM_VK    3 // Send Parameter VK_xxx
> > +
> > +typedef struct {
> > +    BYTE  NLSFEProcIndex;
> > +    ULONG NLSFEProcParam;
> > +} VK_FPARAM;
> > +
> > +typedef struct {
> > +    BYTE Vk;
> > +    BYTE NLSFEProcType;
> > +    BYTE NLSFEProcCurrent;
> > +    BYTE NLSFEProcSwitch;   // 8 bits
> > +    VK_FPARAM NLSFEProc[8];
> > +    VK_FPARAM NLSFEProcAlt[8];
> > +} VK_F;
> > +
> > +typedef struct {
> > +    USHORT OEMIdentifier;
> > +    USHORT LayoutInformation;
> > +    UINT  NumOfVkToF;
> > +    VK_F *pVkToF;
> > +    void *dummy; // used to check size
> > +} KBDNLSTABLES;
> > +
> > +typedef void *WINAPI KbdLayerDescriptor_t(void);
> > +typedef BOOL WINAPI KbdLayerRealDllFile_t(HKL hkl, WCHAR *realDllName,
> > LPVOID pClientKbdType, LPVOID reserve1 , LPVOID reserve2);
> > +typedef KBDNLSTABLES *WINAPI KbdNlsLayerDescriptor_t(void);
> > +
> > +// where all keyboard layouts information are in the registry
> > +#define LAYOUT_REGKEY "SYSTEM\\CurrentControlSet\\Control\\Keyboard
> > Layouts"
> > +
> > +static LONG reg_read_str(HKEY key, LPCWSTR name, WCHAR *value, size_t
> > len);
> > +static HMODULE load_keyboard_layout(const WCHAR *dll_name);
> > +static void keyboard_cache(HKL layout);
> > +
> > +#define test_debug(fmt, ...) do { \
> > +    if (0) printf(fmt "\n", ## __VA_ARGS__); \
> > +} while(0)
> > +
> > +#if KEYBOARD_MODIFIERS_TEST
> > +
> > +#define fatal(fmt, ...) do { fprintf(stderr, fmt "\n", ## __VA_ARGS__);
> > exit(1); } while(0)
> > +
> > +#undef test_debug
> > +#define test_debug(fmt, ...) printf(fmt "\n", ## __VA_ARGS__)
> > +
> > +// this allow to avoid having to link gtk libraries
> > +#undef g_critical
> > +#define g_critical fatal
> > +
> > +static void keyboard_check_single(HKEY key, const char *name);
> > +
> > +// must be tested on
> > +// - WinXP 32
> > +// - Win7 32/64 app
> > +// - Win10 32/64 app
> > +void keyboard_modifiers_test(void)
> > +{
> > +    setbuf(stdout, NULL);
> > +    setbuf(stderr, NULL);
> > +
> > +    // scan all keyboards
> > +    // HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Keyboard
> > Layouts\<00030402>
> > +    // check them all
> > +    HKEY key;
> > +    LONG err;
> > +    err = RegOpenKeyEx(HKEY_LOCAL_MACHINE, LAYOUT_REGKEY, 0, KEY_READ,
> > &key);
> > +    if (err != ERROR_SUCCESS) {
> > +        fatal("RegOpenKeyEx error %ld", err);
> > +        return;
> > +    }
> > +
> > +    unsigned i;
> > +    for (i = 0; ; ++i) {
> > +        char name[64];
> > +        err = RegEnumKey(key, i, name, G_N_ELEMENTS(name));
> > +        if (err == ERROR_NO_MORE_ITEMS)
> > +            break;
> > +        if (err != ERROR_SUCCESS) {
> > +            fatal("RegEnumKey error %ld", err);
> > +            break;
> > +        }
> > +        keyboard_check_single(key, name);
> > +    }
> > +
> > +    RegCloseKey(key);
> > +    // check for multiple keyboards
> > +    //
> > +    // KbdLayerDescriptor - no parameter, returns a table
> > +    // KbdLayerMultiDescriptor
> > +    //   pass a pointer, structure like, output
> > +    //   struct Xxx {
> > +    //      uint32_t num_layout_valid;
> > +    //      struct {
> > +    //          WCHAR dll_name[32];
> > +    //          uint32_t unknown1;
> > +    //          uint32_t unknown2;
> > +    //      } layers[8];
> > +    //   }
> > +    // KbdLayerRealDllFile
> > +    //   BOOL KbdLayerRealDllFile(HKL hkl, WCHAR *realDllName,
> > PCLIENTKEYBOARDTYPE pClientKbdType, LPVOID reserve)
> > +    //   returns TRUE if we need to load another file (this is just a
> > stub)
> > +    //   realDllName returned keyboard name
> > +    //   pClientKbdType used for terminal server, NULL for physical one
> > +    //   reserve NULL
> > +    // KbdLayerRealDllFileNT4 - obsolete
> > +    // KbdNlsLayerDescriptor - no parameter, returns NLS table, if not no
> > need to parse but MapVirtualKey works
> > +    //
> > +}
> > +
> > +static void keyboard_check_single(HKEY key, const char *name)
> > +{
> > +    char *end = NULL;
> > +    errno = 0;
> > +    unsigned long num = strtoul(name, &end, 16);
> > +    if (errno || *end) {
> > +        fatal("wrong value %s", name);
> > +        return;
> > +    }
> > +
> > +    printf("trying keyboard %s\n", name);
> > +    keyboard_cache((HKL) (DWORD_PTR) num);
> > +    printf("----\n");
> > +}
> > +#endif
> > +
> > +/**
> > + * Read a string from registry
> > + * @param key registry key to read from
> > + * @param name name of the value to read
> > + * @param value buffer where to store results
> > + * @param size size of value buffer in bytes (not value elements!)
> > + * @returns system error code or ERROR_SUCCESS
> > + */
> > +static LONG reg_read_str(HKEY key, LPCWSTR name, WCHAR *value, size_t len)
> > +{
> > +    DWORD size = len-sizeof(*value), type;
> > +    LONG err = RegQueryValueExW(key, name, 0, &type, (void *) value,
> > &size);
> > +    if (err != ERROR_SUCCESS)
> > +        return err;
> > +
> > +    if (type != REG_SZ)
> > +        return ERROR_INVALID_DATA;
> > +
> > +    // assure terminated
> > +    value[size/sizeof(*value)] = 0;
> > +    return ERROR_SUCCESS;
> > +}
> > +
> > +/**
> > + * Load a keyboard layout file given the file name
> > + * @param dll_name dll name (should be just file name without paths)
> > + */
> > +static HMODULE load_keyboard_layout(const WCHAR *dll_name)
> > +{
> > +    WCHAR fn[MAX_PATH+256];
> > +
> > +#ifdef _WIN64
> > +    GetSystemDirectoryW(fn, MAX_PATH);
> > +#else
> > +    typedef UINT WINAPI GetSystemWow64DirectoryW_t(LPWSTR str, UINT size);
> > +    GetSystemWow64DirectoryW_t *pGetSystemWow64DirectoryW =
> > +
> > (GetSystemWow64DirectoryW_t*)GetProcAddress(GetModuleHandle("kernel32"),
> > "GetSystemWow64DirectoryW");
> > +    if (!pGetSystemWow64DirectoryW || pGetSystemWow64DirectoryW(fn,
> > MAX_PATH) == 0)
> > +        GetSystemDirectoryW(fn, MAX_PATH);
> > +#endif
> > +    wcscat(fn, L"\\");
> > +    wcscat(fn, dll_name);
> > +
> > +    test_debug("loading file %S", fn);
> > +    return LoadLibraryW(fn);
> > +}
> > +
> > +/**
> > + * Check if current process is running in Wow64 mode
> > + * (32 bit on a 64 system)
> > + */
> > +#ifndef _WIN64
> > +static BOOL is_wow64(void)
> > +{
> > +	BOOL bIsWow64 = FALSE;
> > +
> > +    typedef BOOL WINAPI IsWow64Process_t(HANDLE, PBOOL);
> > +	IsWow64Process_t *pIsWow64Process =
> > +		(IsWow64Process_t *) GetProcAddress(GetModuleHandle("kernel32"),
> > "IsWow64Process");
> > +
> > +	if (pIsWow64Process)
> > +		pIsWow64Process(GetCurrentProcess(), &bIsWow64);
> > +
> > +	return bIsWow64;
> > +}
> > +#else
> > +static inline BOOL is_wow64(void)
> > +{
> > +    return FALSE;
> > +}
> > +#endif
> > +
> > +/**
> > + * Check if OS is 64 bit
> > + */
> > +static BOOL os_is_64bit(void)
> > +{
> > +#ifdef _WIN64
> > +	return TRUE;
> > +#else
> > +	return is_wow64() != FALSE;
> > +#endif
> > +}
> > +
> > +
> > +// keyboard status
> > +// caps lock VK/SC
> > +// combination (ctrl+alt+shift / none)
> > +// ctrl/alt/shift keys VK/SC (2 for each) ?
> > +static WORD vsc_capital = 58;
> > +static int specific_modifiers = -1;
> > +
> > +/**
> > + * Extract information from keyboard.
> > + * Currently scancode of Caps Lock is searched and
> > + * possible modifiers needed to have that Caps Lock.
> > + * @param layout layout to get information
> > + */
> > +static void keyboard_cache(HKL layout)
> > +{
> > +    WCHAR buf[256];
> > +    HKEY key;
> > +    LONG err;
> > +
> > +    KbdLayerDescriptor_t *get_desc;
> > +
> > +    const BYTE *kbd_table;
> > +    int num_keys, i;
> > +    const USHORT *keys;
> > +    const KBDNLSTABLES *nls_table;
> > +    const VK_F *vkf, *vkf_end;
> > +
> > +    // set default output, usually work with lot of keyboard layout
> > +    // these values will be used in case of errors
> > +    vsc_capital = MapVirtualKey(VK_CAPITAL, MAPVK_VK_TO_VSC);
> > +    specific_modifiers = -1;
> > +
> > +    // get keyboard dll name from registry
> > +    swprintf(buf, 256, TEXT(LAYOUT_REGKEY) L"\\%08X",
> > (unsigned)(DWORD_PTR)layout);
> > +    err = RegOpenKeyExW(HKEY_LOCAL_MACHINE, buf, 0, KEY_READ, &key);
> > +    if (err != ERROR_SUCCESS) {
> > +        g_critical("failed getting keyboard layout registry key");
> > +        return;
> > +    }
> > +    err = reg_read_str(key, L"Layout File", buf, sizeof(buf));
> > +    RegCloseKey(key);
> > +    if (err != ERROR_SUCCESS) {
> > +        g_critical("failed getting keyboard layout file name");
> > +        return;
> > +    }
> > +
> > +    // load keyboard layout file
> > +    HMODULE dll = load_keyboard_layout(buf);
> > +    if (!dll) {
> > +        g_critical("error loading keyboard layout for %08x",
> > (unsigned)(DWORD_PTR)layout);
> > +        return;
> > +    }
> > +
> > +    // see if we need to get another dll
> > +    KbdLayerRealDllFile_t *dll_file = (KbdLayerRealDllFile_t *)
> > GetProcAddress(dll, "KbdLayerRealDllFile");
> > +    if (dll_file) {
> > +        // load the other file
> > +        if (dll_file(layout, buf, NULL, NULL, NULL)) {
> > +            test_debug("dll redirected to %S %u", buf, (unsigned)
> > wcslen(buf));
> > +            HMODULE new_dll = load_keyboard_layout(buf);
> > +            if (new_dll) {
> > +                test_debug("unloading stub");
> > +                FreeLibrary(dll);
> > +                dll = new_dll;
> > +            }
> > +        }
> > +    }
> > +
> > +    // check if there are NLS function (in this case we must parse tables)
> > +    KbdNlsLayerDescriptor_t *get_nls_desc = (KbdNlsLayerDescriptor_t *)
> > GetProcAddress(dll, "KbdNlsLayerDescriptor");
> > +    if (!get_nls_desc)
> > +        goto cleanup;
> > +
> > +    // get main keyboard table
> > +    get_desc = (KbdLayerDescriptor_t *) GetProcAddress(dll,
> > "KbdLayerDescriptor");
> > +    if (!get_desc) {
> > +        g_critical("keyboard dll layout has no descriptor");
> > +        goto cleanup;
> > +    }
> > +    kbd_table = (BYTE *) get_desc();
> > +
> > +    // check table (for Win32 see format 32 or 64)
> > +    if (os_is_64bit()) {
> > +        test_debug("64 bit");
> > +        if (IsBadReadPtr(kbd_table, 12*8)) {
> > +            g_critical("wrong table address");
> > +            goto cleanup;
> > +        }
> > +        keys = *((USHORT **) &kbd_table[6*8]);
> > +        num_keys = kbd_table[7*8];
> > +    } else {
> > +        test_debug("32 bit");
> > +        if (IsBadReadPtr(kbd_table, 13*4)) {
> > +            g_critical("wrong table address");
> > +            goto cleanup;
> > +        }
> > +        keys = *((USHORT **) &kbd_table[6*4]);
> > +        num_keys = kbd_table[7*4];
> > +    }
> > +
> > +    // scan VKs for a VK_CAPITAL not special
> > +    if (IsBadReadPtr(keys, num_keys*sizeof(*keys))) {
> > +        g_critical("wrong VKs table");
> > +        goto cleanup;
> > +    }
> > +    for (i = 0; i < num_keys; ++i) {
> > +        // ... return if found
> > +        if ((keys[i] & KBDSPECIAL) == 0 && (keys[i] & 0xFF) == VK_CAPITAL)
> > {
> > +            vsc_capital = i;
> > +            specific_modifiers = -1;
> > +            goto cleanup;
> > +        }
> > +    }
> > +
> > +    // scan NLS table for VK_CAPITALs
> > +    nls_table = get_nls_desc();
> > +    if (IsBadReadPtr(keys, sizeof(*nls_table))) {
> > +        g_critical("wrong NLS table");
> > +        goto cleanup;
> > +    }
> > +    vkf = nls_table->pVkToF;
> > +    if (IsBadReadPtr(vkf, sizeof(*vkf) * nls_table->NumOfVkToF)) {
> > +        g_critical("wrong function table");
> > +        goto cleanup;
> > +    }
> > +    test_debug("layout has %u NLS key", (unsigned) nls_table->NumOfVkToF);
> > +    vkf_end = vkf + nls_table->NumOfVkToF;
> > +    for (; vkf < vkf_end; ++vkf) {
> > +        unsigned mask = 0;
> > +        const VK_FPARAM *params = vkf->NLSFEProc;
> > +
> > +        // scan all functions searching for VK_CAPITAL
> > +        // check both part, normal and with alternate (if present)
> > +        for (i = 0; i < 16; ++i) {
> > +            if ((vkf->Vk == VK_CAPITAL && params[i].NLSFEProcIndex ==
> > KBDNLS_SEND_BASE_VK)
> > +                || (params[i].NLSFEProcIndex == KBDNLS_SEND_PARAM_VK &&
> > params[i].NLSFEProcParam == VK_CAPITAL))
> > +                mask |= 1<<i;
> > +        }
> > +        // no VK_CAPITAL found
> > +        if (!mask)
> > +            continue;
> > +        test_debug("found mask %x at vk %x", mask, vkf->Vk);
> > +        // see if there are a common key between the two tables for
> > +        // each special key
> > +        unsigned common = (mask >> 8) & mask;
> > +        if (common)
> > +            mask = common;
> > +        for (i = 0; i < 16; ++i)
> > +            if (mask & (1<<i)) {
> > +                specific_modifiers = i & 7;
> > +                break;
> > +            }
> > +        // get back base VK
> > +        for (i = 0; i < num_keys; ++i) {
> > +            // ... return if found
> > +            test_debug("keys %d = %x vk %x", i, keys[i], vkf->Vk);
> > +            if ((keys[i] & KBDSPECIAL) != 0 && (keys[i] & 0xFF) ==
> > vkf->Vk)
> > {
> > +                vsc_capital = i;
> > +                goto cleanup;
> > +            }
> > +        }
> > +        // this is unexpected, there should be a key matching the NLS
> > table
> > +        g_critical("NLS key not found in normal table");
> > +    }
> > +    specific_modifiers = -1;
> > +
> > +cleanup:
> > +    test_debug("unloading dll");
> > +    FreeLibrary(dll);
> > +}
> > +
> > +/**
> > + * Add input keys in order to make the special key (shift/control/alt)
> > + * state the same as wanted one.
> > + * @param vk virtual key of the key
> > + * @param curr_state current state (!=0 is key down)
> > + * @param wanted_state wanted state (!=0 is key down)
> > + * @param begin_inputs beginning of already present input keys
> > + * @param end_inputs end of already present input keys
> > + */
> > +static void adjust_special(WORD vk, int curr_state, int wanted_state,
> > +                           INPUT **begin_inputs, INPUT **end_inputs)
> > +{
> > +    KEYBDINPUT *ki;
> > +
> > +    curr_state &= 0x80;
> > +    if (!!wanted_state == !!curr_state)
> > +        return;
> > +
> > +    // if there are not the spcific key no need to handle
> > +    UINT vsc = MapVirtualKey(vk, MAPVK_VK_TO_VSC);
> > +    if (!vsc)
> > +        return;
> > +
> > +    // make sure modifier key is in the right state before pressing
> > +    // main key
> > +    --(*begin_inputs);
> > +    ki = &(*begin_inputs)->ki;
> > +    ki->wVk = vk;
> > +    ki->wScan = vsc;
> > +    ki->dwFlags = wanted_state ? 0 : KEYEVENTF_KEYUP;
> > +
> > +    // make sure key state is restored at the end
> > +    ki = &(*end_inputs)->ki;
> > +    ki->wVk = vk;
> > +    ki->wScan = vsc;
> > +    ki->dwFlags = wanted_state ? KEYEVENTF_KEYUP : 0;
> > +    ++(*end_inputs);
> > +}
> > +
> > +/**
> > + * Add a key pression and a release to inputs events
> > + */
> > +static gboolean add_press_release(INPUT *inputs, WORD vk, WORD vsc)
> > +{
> > +    KEYBDINPUT *ki;
> > +
> > +    if (!vsc)
> > +        return FALSE;
> > +
> > +    ki = &inputs[0].ki;
> > +    ki->wVk = vk;
> > +    ki->wScan = vsc;
> > +    ki = &inputs[1].ki;
> > +    ki->wVk = vk;
> > +    ki->wScan = vsc;
> > +    ki->dwFlags = KEYEVENTF_KEYUP;
> > +
> > +    return TRUE;
> > +}
> > +
> > +void set_keyboard_lock_modifiers(guint32 modifiers)
> > +{
> > +    static HKL cached_layout = 0;
> > +
> > +    // get keyboard layout
> > +    HKL curr_layout = GetKeyboardLayout(0);
> > +
> > +    // same as before, use old informations..
> > +    if (curr_layout != cached_layout) {
> > +        // .. otherwise cache new keyboard layout
> > +        keyboard_cache(curr_layout);
> > +        cached_layout = curr_layout;
> > +    }
> > +
> > +    BYTE key_states[256];
> > +    GetKeyboardState(key_states);
> > +
> > +    // compute sequence to press
> > +    // as documented in SetKeyboardState we must press
> > +    // the sequence that cause the state to change
> > +    // to modify the global state
> > +    int i;
> > +    INPUT inputs[6*2+2+4], *begin_inputs, *end_inputs;
> > +    memset(inputs, 0, sizeof(inputs));
> > +    for (i = 0; i < G_N_ELEMENTS(inputs); ++i) {
> > +        inputs[i].type = INPUT_KEYBOARD;
> > +        inputs[i].ki.dwExtraInfo = 0x12345678;
> > +    }
> > +
> > +    // start pointers, make sure we have enough space
> > +    // before and after to insert shift/control/alt keys
> > +    begin_inputs = end_inputs = inputs + 6;
> > +
> > +    // we surely must press the caps lock key
> > +    if ((!!(modifiers & SPICE_INPUTS_CAPS_LOCK) !=
> > !!(key_states[VK_CAPITAL]
> > & 0x80))
> > +        && add_press_release(end_inputs, VK_CAPITAL, vsc_capital)) {
> > +        end_inputs += 2;
> > +
> > +        // unfortunately the key expect a specific combination
> > +        // of shift/control/alt, make sure we have that state
> > +        if (specific_modifiers >= 0) {
> > +
> > +#define adjust_special(vk, mask)  \
> > +    adjust_special((vk), key_states[vk], specific_modifiers & (mask),
> > &begin_inputs, &end_inputs)
> > +
> > +            adjust_special(VK_LSHIFT, 1);
> > +            adjust_special(VK_RSHIFT, 1);
> > +            adjust_special(VK_LCONTROL, 2);
> > +            adjust_special(VK_RCONTROL, 2);
> > +            adjust_special(VK_LMENU, 4);
> > +            adjust_special(VK_RMENU, 4);
> > +        }
> > +    }
> > +
> > +    // sync NUMLOCK
> > +    if ((!!(modifiers & SPICE_INPUTS_NUM_LOCK) !=
> > !!(key_states[VK_NUMLOCK]
> > & 0x80))
> > +        && add_press_release(end_inputs, VK_NUMLOCK,
> > MapVirtualKey(VK_NUMLOCK, MAPVK_VK_TO_VSC))) {
> > +        end_inputs += 2;
> > +    }
> > +
> > +    // sync SCROLLLOCK
> > +    if ((!!(modifiers & SPICE_INPUTS_SCROLL_LOCK) !=
> > !!(key_states[VK_SCROLL] & 0x80))
> > +        && add_press_release(end_inputs, VK_SCROLL,
> > MapVirtualKey(VK_SCROLL,
> > MAPVK_VK_TO_VSC))) {
> > +        end_inputs += 2;
> > +    }
> > +
> > +    // press the sequence
> > +    if (end_inputs > begin_inputs) {
> > +        BlockInput(TRUE);
> > +        SendInput(end_inputs - begin_inputs, begin_inputs,
> > sizeof(inputs[0]));
> > +        BlockInput(FALSE);
> > +    }
> > +}
> > +
> >  #else
> >  
> >  void set_keyboard_lock_modifiers(guint32 modifiers)
> > diff --git a/tests/Makefile.am b/tests/Makefile.am
> > index 1a8b768..f96ee48 100644
> > --- a/tests/Makefile.am
> > +++ b/tests/Makefile.am
> > @@ -16,6 +16,21 @@ TESTS += usb-acl-helper
> >  noinst_PROGRAMS += mock-acl-helper
> >  endif
> >  
> > +if OS_WIN32
> > +TESTS += keyboard-test
> > +keyboard_test_SOURCES = \
> > +	keyboard-test.c \
> > +	$(NULL)
> > +keyboard_test_CPPFLAGS = \
> > +	$(COMMON_CFLAGS)			\
> > +	-DSPICE_COMPILATION					\
> > +	$(GTK_CFLAGS)						\
> > +	-DG_LOG_DOMAIN=\"GSpice\"		\
> > +	$(NULL)
> > +keyboard_test_LDADD = \
> > +	$(GTK_LIBS)			\
> > +	$(NULL)
> > +endif
> >  noinst_PROGRAMS += $(TESTS)
> >  
> >  AM_CPPFLAGS =					\
> > diff --git a/tests/keyboard-test.c b/tests/keyboard-test.c
> > new file mode 100644
> > index 0000000..9c7dfba
> > --- /dev/null
> > +++ b/tests/keyboard-test.c
> > @@ -0,0 +1,9 @@
> > +#define KEYBOARD_MODIFIERS_TEST 1
> > +
> > +#include "../src/keyboard-modifiers.c"
> 
> I would rather not include the C file, but have the test code in this file
> (like the rest of the tests)
> 

Yes, quite a bad hack.

There were some reason to do these hacks:
- having an executable not relying on Gtk so to have to copy a single
  .exe file and launch it;
- use a static, not exported, function in spice-gtk;
- avoid to add ../src/keyboard-modifiers.c in tests/Makefile.am which
  give errors.

First point is no more a big issue (already tested).

I'm pondering (beside moving test functions) to add a library to
src/Makefile.am to provide functions required (keyboard_cache).

Any opinion?

Frediano

> > +
> > +int main(void)
> > +{
> > +    keyboard_modifiers_test();
> > +    return 0;
> > +}



More information about the Spice-devel mailing list