mirror of
https://github.com/debauchee/barrier.git
synced 2025-10-26 11:17:49 +00:00
Many tools strip trailing whitespaces by default, so after editing a file with
whitespace errors we end up with a bunch of unrelated hunks that need to be
reverted locally.
This could be fixed by configuring each tool to not do this (at least for the
barrier repo), or, simpler, we just sed the problem away and make barrier
whitespace-compliant.
sed commands run:
sed -i 's/[ \t]\+$//' **/*.(cpp|h) **/*CMakeLists.txt
Verified with git diff --ignore-space-change, this shows the empty diff.
1956 lines
55 KiB
C++
1956 lines
55 KiB
C++
/*
|
|
* barrier -- mouse and keyboard sharing utility
|
|
* Copyright (C) 2018 Debauchee Open Source Group
|
|
* Copyright (C) 2012-2016 Symless Ltd.
|
|
* Copyright (C) 2002 Chris Schoeneman
|
|
*
|
|
* This package is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* found in the file LICENSE that should have accompanied this file.
|
|
*
|
|
* This package is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "platform/MSWindowsScreen.h"
|
|
|
|
#include "platform/MSWindowsDropTarget.h"
|
|
#include "client/Client.h"
|
|
#include "platform/MSWindowsClipboard.h"
|
|
#include "platform/MSWindowsDesks.h"
|
|
#include "platform/MSWindowsEventQueueBuffer.h"
|
|
#include "platform/MSWindowsKeyState.h"
|
|
#include "platform/MSWindowsScreenSaver.h"
|
|
#include "barrier/Clipboard.h"
|
|
#include "barrier/KeyMap.h"
|
|
#include "barrier/XScreen.h"
|
|
#include "barrier/App.h"
|
|
#include "barrier/ArgsBase.h"
|
|
#include "barrier/ClientApp.h"
|
|
#include "mt/Lock.h"
|
|
#include "mt/Thread.h"
|
|
#include "arch/win32/ArchMiscWindows.h"
|
|
#include "arch/Arch.h"
|
|
#include "base/FunctionJob.h"
|
|
#include "base/Log.h"
|
|
#include "base/IEventQueue.h"
|
|
#include "base/TMethodEventJob.h"
|
|
#include "base/TMethodJob.h"
|
|
|
|
#include <string.h>
|
|
#include <Shlobj.h>
|
|
#include <comutil.h>
|
|
#include <algorithm>
|
|
|
|
//
|
|
// add backwards compatible multihead support (and suppress bogus warning).
|
|
// this isn't supported on MinGW yet AFAICT.
|
|
//
|
|
#if defined(_MSC_VER)
|
|
#pragma warning(push)
|
|
#pragma warning(disable: 4706) // assignment within conditional
|
|
#define COMPILE_MULTIMON_STUBS
|
|
#include <multimon.h>
|
|
#pragma warning(pop)
|
|
#endif
|
|
|
|
// X button stuff
|
|
#if !defined(WM_XBUTTONDOWN)
|
|
#define WM_XBUTTONDOWN 0x020B
|
|
#define WM_XBUTTONUP 0x020C
|
|
#define WM_XBUTTONDBLCLK 0x020D
|
|
#define WM_NCXBUTTONDOWN 0x00AB
|
|
#define WM_NCXBUTTONUP 0x00AC
|
|
#define WM_NCXBUTTONDBLCLK 0x00AD
|
|
#define MOUSEEVENTF_XDOWN 0x0080
|
|
#define MOUSEEVENTF_XUP 0x0100
|
|
#define XBUTTON1 0x0001
|
|
#define XBUTTON2 0x0002
|
|
#endif
|
|
#if !defined(VK_XBUTTON1)
|
|
#define VK_XBUTTON1 0x05
|
|
#define VK_XBUTTON2 0x06
|
|
#endif
|
|
|
|
// WM_POWERBROADCAST stuff
|
|
#if !defined(PBT_APMRESUMEAUTOMATIC)
|
|
#define PBT_APMRESUMEAUTOMATIC 0x0012
|
|
#endif
|
|
|
|
//
|
|
// MSWindowsScreen
|
|
//
|
|
|
|
HINSTANCE MSWindowsScreen::s_windowInstance = NULL;
|
|
MSWindowsScreen* MSWindowsScreen::s_screen = NULL;
|
|
|
|
MSWindowsScreen::MSWindowsScreen(
|
|
bool isPrimary,
|
|
bool noHooks,
|
|
bool stopOnDeskSwitch,
|
|
IEventQueue* events) :
|
|
PlatformScreen(events),
|
|
m_isPrimary(isPrimary),
|
|
m_noHooks(noHooks),
|
|
m_isOnScreen(m_isPrimary),
|
|
m_class(0),
|
|
m_x(0), m_y(0),
|
|
m_w(0), m_h(0),
|
|
m_xCenter(0), m_yCenter(0),
|
|
m_multimon(false),
|
|
m_xCursor(0), m_yCursor(0),
|
|
m_sequenceNumber(0),
|
|
m_mark(0),
|
|
m_markReceived(0),
|
|
m_fixTimer(NULL),
|
|
m_keyLayout(NULL),
|
|
m_screensaver(NULL),
|
|
m_screensaverNotify(false),
|
|
m_screensaverActive(false),
|
|
m_window(NULL),
|
|
m_nextClipboardWindow(NULL),
|
|
m_ownClipboard(false),
|
|
m_desks(NULL),
|
|
m_keyState(NULL),
|
|
m_hasMouse(GetSystemMetrics(SM_MOUSEPRESENT) != 0),
|
|
m_showingMouse(false),
|
|
m_events(events),
|
|
m_dropWindow(NULL),
|
|
m_dropWindowSize(20)
|
|
{
|
|
assert(s_windowInstance != NULL);
|
|
assert(s_screen == NULL);
|
|
|
|
s_screen = this;
|
|
try {
|
|
m_screensaver = new MSWindowsScreenSaver();
|
|
m_desks = new MSWindowsDesks(
|
|
m_isPrimary,
|
|
m_noHooks,
|
|
m_screensaver,
|
|
m_events,
|
|
new TMethodJob<MSWindowsScreen>(
|
|
this, &MSWindowsScreen::updateKeysCB),
|
|
stopOnDeskSwitch);
|
|
m_keyState = new MSWindowsKeyState(m_desks, getEventTarget(), m_events);
|
|
|
|
updateScreenShape();
|
|
m_class = createWindowClass();
|
|
m_window = createWindow(m_class, "Barrier");
|
|
forceShowCursor();
|
|
LOG((CLOG_DEBUG "screen shape: %d,%d %dx%d %s", m_x, m_y, m_w, m_h, m_multimon ? "(multi-monitor)" : ""));
|
|
LOG((CLOG_DEBUG "window is 0x%08x", m_window));
|
|
|
|
// SHGetFolderPath is deprecated in vista, but use it for xp support.
|
|
char desktopPath[MAX_PATH];
|
|
if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_DESKTOP, NULL, 0, desktopPath))) {
|
|
m_desktopPath = std::string(desktopPath);
|
|
LOG((CLOG_DEBUG "using desktop for drop target: %s", m_desktopPath.c_str()));
|
|
}
|
|
else {
|
|
LOG((CLOG_ERR "failed to get desktop path, no drop target available, error=%d", GetLastError()));
|
|
}
|
|
|
|
OleInitialize(0);
|
|
m_dropWindow = createDropWindow(m_class, "DropWindow");
|
|
m_dropTarget = new MSWindowsDropTarget();
|
|
RegisterDragDrop(m_dropWindow, m_dropTarget);
|
|
}
|
|
catch (...) {
|
|
delete m_keyState;
|
|
delete m_desks;
|
|
delete m_screensaver;
|
|
destroyWindow(m_window);
|
|
destroyClass(m_class);
|
|
s_screen = NULL;
|
|
throw;
|
|
}
|
|
|
|
// install event handlers
|
|
m_events->adoptHandler(Event::kSystem, m_events->getSystemTarget(),
|
|
new TMethodEventJob<MSWindowsScreen>(this,
|
|
&MSWindowsScreen::handleSystemEvent));
|
|
|
|
// install the platform event queue
|
|
m_events->adoptBuffer(new MSWindowsEventQueueBuffer(m_events));
|
|
}
|
|
|
|
MSWindowsScreen::~MSWindowsScreen()
|
|
{
|
|
assert(s_screen != NULL);
|
|
|
|
disable();
|
|
m_events->adoptBuffer(NULL);
|
|
m_events->removeHandler(Event::kSystem, m_events->getSystemTarget());
|
|
delete m_keyState;
|
|
delete m_desks;
|
|
delete m_screensaver;
|
|
destroyWindow(m_window);
|
|
destroyClass(m_class);
|
|
|
|
RevokeDragDrop(m_dropWindow);
|
|
m_dropTarget->Release();
|
|
OleUninitialize();
|
|
destroyWindow(m_dropWindow);
|
|
|
|
s_screen = NULL;
|
|
}
|
|
|
|
void
|
|
MSWindowsScreen::init(HINSTANCE windowInstance)
|
|
{
|
|
assert(s_windowInstance == NULL);
|
|
assert(windowInstance != NULL);
|
|
|
|
s_windowInstance = windowInstance;
|
|
}
|
|
|
|
HINSTANCE
|
|
MSWindowsScreen::getWindowInstance()
|
|
{
|
|
return s_windowInstance;
|
|
}
|
|
|
|
void
|
|
MSWindowsScreen::enable()
|
|
{
|
|
assert(m_isOnScreen == m_isPrimary);
|
|
|
|
// we need to poll some things to fix them
|
|
m_fixTimer = m_events->newTimer(1.0, NULL);
|
|
m_events->adoptHandler(Event::kTimer, m_fixTimer,
|
|
new TMethodEventJob<MSWindowsScreen>(this,
|
|
&MSWindowsScreen::handleFixes));
|
|
|
|
// install our clipboard snooper
|
|
m_nextClipboardWindow = SetClipboardViewer(m_window);
|
|
|
|
// track the active desk and (re)install the hooks
|
|
m_desks->enable();
|
|
|
|
if (m_isPrimary) {
|
|
// set jump zones
|
|
m_hook.setZone(m_x, m_y, m_w, m_h, getJumpZoneSize());
|
|
|
|
// watch jump zones
|
|
m_hook.setMode(kHOOK_WATCH_JUMP_ZONE);
|
|
}
|
|
else {
|
|
// prevent the system from entering power saving modes. if
|
|
// it did we'd be forced to disconnect from the server and
|
|
// the server would not be able to wake us up.
|
|
ArchMiscWindows::addBusyState(ArchMiscWindows::kSYSTEM);
|
|
}
|
|
}
|
|
|
|
void
|
|
MSWindowsScreen::disable()
|
|
{
|
|
// stop tracking the active desk
|
|
m_desks->disable();
|
|
|
|
if (m_isPrimary) {
|
|
// disable hooks
|
|
m_hook.setMode(kHOOK_DISABLE);
|
|
|
|
// enable special key sequences on win95 family
|
|
enableSpecialKeys(true);
|
|
}
|
|
else {
|
|
// allow the system to enter power saving mode
|
|
ArchMiscWindows::removeBusyState(ArchMiscWindows::kSYSTEM |
|
|
ArchMiscWindows::kDISPLAY);
|
|
}
|
|
|
|
// tell key state
|
|
m_keyState->disable();
|
|
|
|
// stop snooping the clipboard
|
|
ChangeClipboardChain(m_window, m_nextClipboardWindow);
|
|
m_nextClipboardWindow = NULL;
|
|
|
|
// uninstall fix timer
|
|
if (m_fixTimer != NULL) {
|
|
m_events->removeHandler(Event::kTimer, m_fixTimer);
|
|
m_events->deleteTimer(m_fixTimer);
|
|
m_fixTimer = NULL;
|
|
}
|
|
|
|
m_isOnScreen = m_isPrimary;
|
|
forceShowCursor();
|
|
}
|
|
|
|
void
|
|
MSWindowsScreen::enter()
|
|
{
|
|
m_desks->enter();
|
|
if (m_isPrimary) {
|
|
// enable special key sequences on win95 family
|
|
enableSpecialKeys(true);
|
|
|
|
// watch jump zones
|
|
m_hook.setMode(kHOOK_WATCH_JUMP_ZONE);
|
|
|
|
// all messages prior to now are invalid
|
|
nextMark();
|
|
|
|
m_primaryKeyDownList.clear();
|
|
}
|
|
else {
|
|
// Entering a secondary screen. Ensure that no screensaver is active
|
|
// and that the screen is not in powersave mode.
|
|
ArchMiscWindows::wakeupDisplay();
|
|
|
|
if (m_screensaver != NULL && m_screensaverActive)
|
|
{
|
|
m_screensaver->deactivate();
|
|
m_screensaverActive = 0;
|
|
}
|
|
}
|
|
|
|
// now on screen
|
|
m_isOnScreen = true;
|
|
forceShowCursor();
|
|
}
|
|
|
|
bool
|
|
MSWindowsScreen::leave()
|
|
{
|
|
// get keyboard layout of foreground window. we'll use this
|
|
// keyboard layout for translating keys sent to clients.
|
|
HWND window = GetForegroundWindow();
|
|
DWORD thread = GetWindowThreadProcessId(window, NULL);
|
|
m_keyLayout = GetKeyboardLayout(thread);
|
|
|
|
// tell the key mapper about the keyboard layout
|
|
m_keyState->setKeyLayout(m_keyLayout);
|
|
|
|
// tell desk that we're leaving and tell it the keyboard layout
|
|
m_desks->leave(m_keyLayout);
|
|
|
|
if (m_isPrimary) {
|
|
|
|
// warp to center
|
|
LOG((CLOG_DEBUG1 "warping cursor to center: %+d, %+d", m_xCenter, m_yCenter));
|
|
warpCursor(m_xCenter, m_yCenter);
|
|
|
|
// disable special key sequences on win95 family
|
|
enableSpecialKeys(false);
|
|
|
|
// all messages prior to now are invalid
|
|
nextMark();
|
|
|
|
// remember the modifier state. this is the modifier state
|
|
// reflected in the internal keyboard state.
|
|
m_keyState->saveModifiers();
|
|
|
|
m_hook.setMode(kHOOK_RELAY_EVENTS);
|
|
|
|
m_primaryKeyDownList.clear();
|
|
for (KeyButton i = 0; i < IKeyState::kNumButtons; ++i) {
|
|
if (m_keyState->isKeyDown(i)) {
|
|
m_primaryKeyDownList.push_back(i);
|
|
LOG((CLOG_DEBUG1 "key button %d is down before leaving to another screen", i));
|
|
}
|
|
}
|
|
}
|
|
|
|
// now off screen
|
|
m_isOnScreen = false;
|
|
forceShowCursor();
|
|
|
|
if (isDraggingStarted() && !m_isPrimary) {
|
|
m_sendDragThread = new Thread(
|
|
new TMethodJob<MSWindowsScreen>(
|
|
this,
|
|
&MSWindowsScreen::sendDragThread));
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void
|
|
MSWindowsScreen::sendDragThread(void*)
|
|
{
|
|
std::string& draggingFilename = getDraggingFilename();
|
|
size_t size = draggingFilename.size();
|
|
|
|
if (draggingFilename.empty() == false) {
|
|
ClientApp& app = ClientApp::instance();
|
|
Client* client = app.getClientPtr();
|
|
UInt32 fileCount = 1;
|
|
LOG((CLOG_DEBUG "send dragging info to server: %s", draggingFilename.c_str()));
|
|
client->sendDragInfo(fileCount, draggingFilename, size);
|
|
LOG((CLOG_DEBUG "send dragging file to server"));
|
|
client->sendFileToServer(draggingFilename.c_str());
|
|
}
|
|
|
|
m_draggingStarted = false;
|
|
}
|
|
|
|
bool
|
|
MSWindowsScreen::setClipboard(ClipboardID, const IClipboard* src)
|
|
{
|
|
MSWindowsClipboard dst(m_window);
|
|
if (src != NULL) {
|
|
// save clipboard data
|
|
return Clipboard::copy(&dst, src);
|
|
}
|
|
else {
|
|
// assert clipboard ownership
|
|
if (!dst.open(0)) {
|
|
return false;
|
|
}
|
|
dst.empty();
|
|
dst.close();
|
|
return true;
|
|
}
|
|
}
|
|
|
|
void
|
|
MSWindowsScreen::checkClipboards()
|
|
{
|
|
// if we think we own the clipboard but we don't then somebody
|
|
// grabbed the clipboard on this screen without us knowing.
|
|
// tell the server that this screen grabbed the clipboard.
|
|
//
|
|
// this works around bugs in the clipboard viewer chain.
|
|
// sometimes NT will simply never send WM_DRAWCLIPBOARD
|
|
// messages for no apparent reason and rebooting fixes the
|
|
// problem. since we don't want a broken clipboard until the
|
|
// next reboot we do this double check. clipboard ownership
|
|
// won't be reflected on other screens until we leave but at
|
|
// least the clipboard itself will work.
|
|
if (m_ownClipboard && !MSWindowsClipboard::isOwnedByBarrier()) {
|
|
LOG((CLOG_DEBUG "clipboard changed: lost ownership and no notification received"));
|
|
m_ownClipboard = false;
|
|
sendClipboardEvent(m_events->forClipboard().clipboardGrabbed(), kClipboardClipboard);
|
|
sendClipboardEvent(m_events->forClipboard().clipboardGrabbed(), kClipboardSelection);
|
|
}
|
|
}
|
|
|
|
void
|
|
MSWindowsScreen::openScreensaver(bool notify)
|
|
{
|
|
assert(m_screensaver != NULL);
|
|
|
|
m_screensaverNotify = notify;
|
|
if (m_screensaverNotify) {
|
|
m_desks->installScreensaverHooks(true);
|
|
}
|
|
else if (m_screensaver) {
|
|
m_screensaver->disable();
|
|
}
|
|
}
|
|
|
|
void
|
|
MSWindowsScreen::closeScreensaver()
|
|
{
|
|
if (m_screensaver != NULL) {
|
|
if (m_screensaverNotify) {
|
|
m_desks->installScreensaverHooks(false);
|
|
}
|
|
else {
|
|
m_screensaver->enable();
|
|
}
|
|
}
|
|
m_screensaverNotify = false;
|
|
}
|
|
|
|
void
|
|
MSWindowsScreen::screensaver(bool activate)
|
|
{
|
|
assert(m_screensaver != NULL);
|
|
if (m_screensaver==NULL) return;
|
|
|
|
if (activate) {
|
|
m_screensaver->activate();
|
|
}
|
|
else {
|
|
m_screensaver->deactivate();
|
|
}
|
|
}
|
|
|
|
void
|
|
MSWindowsScreen::resetOptions()
|
|
{
|
|
m_desks->resetOptions();
|
|
}
|
|
|
|
void
|
|
MSWindowsScreen::setOptions(const OptionsList& options)
|
|
{
|
|
m_desks->setOptions(options);
|
|
}
|
|
|
|
void
|
|
MSWindowsScreen::setSequenceNumber(UInt32 seqNum)
|
|
{
|
|
m_sequenceNumber = seqNum;
|
|
}
|
|
|
|
bool
|
|
MSWindowsScreen::isPrimary() const
|
|
{
|
|
return m_isPrimary;
|
|
}
|
|
|
|
void*
|
|
MSWindowsScreen::getEventTarget() const
|
|
{
|
|
return const_cast<MSWindowsScreen*>(this);
|
|
}
|
|
|
|
bool
|
|
MSWindowsScreen::getClipboard(ClipboardID, IClipboard* dst) const
|
|
{
|
|
MSWindowsClipboard src(m_window);
|
|
Clipboard::copy(dst, &src);
|
|
return true;
|
|
}
|
|
|
|
void
|
|
MSWindowsScreen::getShape(SInt32& x, SInt32& y, SInt32& w, SInt32& h) const
|
|
{
|
|
assert(m_class != 0);
|
|
|
|
x = m_x;
|
|
y = m_y;
|
|
w = m_w;
|
|
h = m_h;
|
|
}
|
|
|
|
void
|
|
MSWindowsScreen::getCursorPos(SInt32& x, SInt32& y) const
|
|
{
|
|
m_desks->getCursorPos(x, y);
|
|
}
|
|
|
|
void
|
|
MSWindowsScreen::reconfigure(UInt32 activeSides)
|
|
{
|
|
assert(m_isPrimary);
|
|
|
|
LOG((CLOG_DEBUG "active sides: %x", activeSides));
|
|
m_hook.setSides(activeSides);
|
|
}
|
|
|
|
void
|
|
MSWindowsScreen::warpCursor(SInt32 x, SInt32 y)
|
|
{
|
|
// warp mouse
|
|
warpCursorNoFlush(x, y);
|
|
|
|
// remove all input events before and including warp
|
|
MSG msg;
|
|
while (PeekMessage(&msg, NULL, BARRIER_MSG_INPUT_FIRST,
|
|
BARRIER_MSG_INPUT_LAST, PM_REMOVE)) {
|
|
// do nothing
|
|
}
|
|
|
|
// save position to compute delta of next motion
|
|
saveMousePosition(x, y);
|
|
}
|
|
|
|
void MSWindowsScreen::saveMousePosition(SInt32 x, SInt32 y) {
|
|
m_xCursor = x;
|
|
m_yCursor = y;
|
|
|
|
LOG((CLOG_DEBUG5 "saved mouse position for next delta: %+d,%+d", x,y));
|
|
}
|
|
|
|
UInt32
|
|
MSWindowsScreen::registerHotKey(KeyID key, KeyModifierMask mask)
|
|
{
|
|
// only allow certain modifiers
|
|
if ((mask & ~(KeyModifierShift | KeyModifierControl |
|
|
KeyModifierAlt | KeyModifierSuper)) != 0) {
|
|
// this should be a warning, but this can confuse users,
|
|
// as this warning happens almost always.
|
|
LOG((CLOG_DEBUG "could not map hotkey id=%04x mask=%04x", key, mask));
|
|
return 0;
|
|
}
|
|
|
|
// fail if no keys
|
|
if (key == kKeyNone && mask == 0) {
|
|
return 0;
|
|
}
|
|
|
|
// convert to win32
|
|
UINT modifiers = 0;
|
|
if ((mask & KeyModifierShift) != 0) {
|
|
modifiers |= MOD_SHIFT;
|
|
}
|
|
if ((mask & KeyModifierControl) != 0) {
|
|
modifiers |= MOD_CONTROL;
|
|
}
|
|
if ((mask & KeyModifierAlt) != 0) {
|
|
modifiers |= MOD_ALT;
|
|
}
|
|
if ((mask & KeyModifierSuper) != 0) {
|
|
modifiers |= MOD_WIN;
|
|
}
|
|
UINT vk = m_keyState->mapKeyToVirtualKey(key);
|
|
if (key != kKeyNone && vk == 0) {
|
|
// can't map key
|
|
// this should be a warning, but this can confuse users,
|
|
// as this warning happens almost always.
|
|
LOG((CLOG_DEBUG "could not map hotkey id=%04x mask=%04x", key, mask));
|
|
return 0;
|
|
}
|
|
|
|
// choose hotkey id
|
|
UInt32 id;
|
|
if (!m_oldHotKeyIDs.empty()) {
|
|
id = m_oldHotKeyIDs.back();
|
|
m_oldHotKeyIDs.pop_back();
|
|
}
|
|
else {
|
|
//id = m_hotKeys.size() + 1;
|
|
id = (UInt32)m_hotKeys.size() + 1;
|
|
}
|
|
|
|
// if this hot key has modifiers only then we'll handle it specially
|
|
bool err;
|
|
if (key == kKeyNone) {
|
|
// check if already registered
|
|
err = (m_hotKeyToIDMap.count(HotKeyItem(vk, modifiers)) > 0);
|
|
}
|
|
else {
|
|
// register with OS
|
|
err = (RegisterHotKey(NULL, id, modifiers, vk) == 0);
|
|
}
|
|
|
|
if (!err) {
|
|
m_hotKeys.insert(std::make_pair(id, HotKeyItem(vk, modifiers)));
|
|
m_hotKeyToIDMap[HotKeyItem(vk, modifiers)] = id;
|
|
}
|
|
else {
|
|
m_oldHotKeyIDs.push_back(id);
|
|
m_hotKeys.erase(id);
|
|
LOG((CLOG_WARN "failed to register hotkey %s (id=%04x mask=%04x)", barrier::KeyMap::formatKey(key, mask).c_str(), key, mask));
|
|
return 0;
|
|
}
|
|
|
|
LOG((CLOG_DEBUG "registered hotkey %s (id=%04x mask=%04x) as id=%d", barrier::KeyMap::formatKey(key, mask).c_str(), key, mask, id));
|
|
return id;
|
|
}
|
|
|
|
void
|
|
MSWindowsScreen::unregisterHotKey(UInt32 id)
|
|
{
|
|
// look up hotkey
|
|
HotKeyMap::iterator i = m_hotKeys.find(id);
|
|
if (i == m_hotKeys.end()) {
|
|
return;
|
|
}
|
|
|
|
// unregister with OS
|
|
bool err;
|
|
if (i->second.getVirtualKey() != 0) {
|
|
err = !UnregisterHotKey(NULL, id);
|
|
}
|
|
else {
|
|
err = false;
|
|
}
|
|
if (err) {
|
|
LOG((CLOG_WARN "failed to unregister hotkey id=%d", id));
|
|
}
|
|
else {
|
|
LOG((CLOG_DEBUG "unregistered hotkey id=%d", id));
|
|
}
|
|
|
|
// discard hot key from map and record old id for reuse
|
|
m_hotKeyToIDMap.erase(i->second);
|
|
m_hotKeys.erase(i);
|
|
m_oldHotKeyIDs.push_back(id);
|
|
}
|
|
|
|
void
|
|
MSWindowsScreen::fakeInputBegin()
|
|
{
|
|
assert(m_isPrimary);
|
|
|
|
if (!m_isOnScreen) {
|
|
m_keyState->useSavedModifiers(true);
|
|
}
|
|
m_desks->fakeInputBegin();
|
|
}
|
|
|
|
void
|
|
MSWindowsScreen::fakeInputEnd()
|
|
{
|
|
assert(m_isPrimary);
|
|
|
|
m_desks->fakeInputEnd();
|
|
if (!m_isOnScreen) {
|
|
m_keyState->useSavedModifiers(false);
|
|
}
|
|
}
|
|
|
|
SInt32
|
|
MSWindowsScreen::getJumpZoneSize() const
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
bool
|
|
MSWindowsScreen::isAnyMouseButtonDown(UInt32& buttonID) const
|
|
{
|
|
static const char* buttonToName[] = {
|
|
"<invalid>",
|
|
"Left Button",
|
|
"Middle Button",
|
|
"Right Button",
|
|
"X Button 1",
|
|
"X Button 2"
|
|
};
|
|
|
|
for (UInt32 i = 1; i < sizeof(m_buttons) / sizeof(m_buttons[0]); ++i) {
|
|
if (m_buttons[i]) {
|
|
buttonID = i;
|
|
LOG((CLOG_DEBUG "locked by \"%s\"", buttonToName[i]));
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void
|
|
MSWindowsScreen::getCursorCenter(SInt32& x, SInt32& y) const
|
|
{
|
|
x = m_xCenter;
|
|
y = m_yCenter;
|
|
}
|
|
|
|
void
|
|
MSWindowsScreen::fakeMouseButton(ButtonID id, bool press)
|
|
{
|
|
m_desks->fakeMouseButton(id, press);
|
|
|
|
if (id == kButtonLeft) {
|
|
if (press) {
|
|
m_buttons[kButtonLeft] = true;
|
|
}
|
|
else {
|
|
m_buttons[kButtonLeft] = false;
|
|
m_fakeDraggingStarted = false;
|
|
m_draggingStarted = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
MSWindowsScreen::fakeMouseMove(SInt32 x, SInt32 y)
|
|
{
|
|
m_desks->fakeMouseMove(x, y);
|
|
if (m_buttons[kButtonLeft]) {
|
|
m_draggingStarted = true;
|
|
}
|
|
}
|
|
|
|
void
|
|
MSWindowsScreen::fakeMouseRelativeMove(SInt32 dx, SInt32 dy) const
|
|
{
|
|
m_desks->fakeMouseRelativeMove(dx, dy);
|
|
}
|
|
|
|
void
|
|
MSWindowsScreen::fakeMouseWheel(SInt32 xDelta, SInt32 yDelta) const
|
|
{
|
|
m_desks->fakeMouseWheel(xDelta, yDelta);
|
|
}
|
|
|
|
void
|
|
MSWindowsScreen::updateKeys()
|
|
{
|
|
m_desks->updateKeys();
|
|
}
|
|
|
|
void
|
|
MSWindowsScreen::fakeKeyDown(KeyID id, KeyModifierMask mask,
|
|
KeyButton button)
|
|
{
|
|
PlatformScreen::fakeKeyDown(id, mask, button);
|
|
updateForceShowCursor();
|
|
}
|
|
|
|
bool
|
|
MSWindowsScreen::fakeKeyRepeat(KeyID id, KeyModifierMask mask,
|
|
SInt32 count, KeyButton button)
|
|
{
|
|
bool result = PlatformScreen::fakeKeyRepeat(id, mask, count, button);
|
|
updateForceShowCursor();
|
|
return result;
|
|
}
|
|
|
|
bool
|
|
MSWindowsScreen::fakeKeyUp(KeyButton button)
|
|
{
|
|
bool result = PlatformScreen::fakeKeyUp(button);
|
|
updateForceShowCursor();
|
|
return result;
|
|
}
|
|
|
|
void
|
|
MSWindowsScreen::fakeAllKeysUp()
|
|
{
|
|
PlatformScreen::fakeAllKeysUp();
|
|
updateForceShowCursor();
|
|
}
|
|
|
|
HCURSOR
|
|
MSWindowsScreen::createBlankCursor() const
|
|
{
|
|
// create a transparent cursor
|
|
int cw = GetSystemMetrics(SM_CXCURSOR);
|
|
int ch = GetSystemMetrics(SM_CYCURSOR);
|
|
|
|
UInt8* cursorAND = new UInt8[ch * ((cw + 31) >> 2)];
|
|
UInt8* cursorXOR = new UInt8[ch * ((cw + 31) >> 2)];
|
|
memset(cursorAND, 0xff, ch * ((cw + 31) >> 2));
|
|
memset(cursorXOR, 0x00, ch * ((cw + 31) >> 2));
|
|
HCURSOR c = CreateCursor(s_windowInstance, 0, 0, cw, ch, cursorAND, cursorXOR);
|
|
delete[] cursorXOR;
|
|
delete[] cursorAND;
|
|
return c;
|
|
}
|
|
|
|
void
|
|
MSWindowsScreen::destroyCursor(HCURSOR cursor) const
|
|
{
|
|
if (cursor != NULL) {
|
|
DestroyCursor(cursor);
|
|
}
|
|
}
|
|
|
|
ATOM
|
|
MSWindowsScreen::createWindowClass() const
|
|
{
|
|
WNDCLASSEX classInfo;
|
|
classInfo.cbSize = sizeof(classInfo);
|
|
classInfo.style = CS_DBLCLKS | CS_NOCLOSE;
|
|
classInfo.lpfnWndProc = &MSWindowsScreen::wndProc;
|
|
classInfo.cbClsExtra = 0;
|
|
classInfo.cbWndExtra = 0;
|
|
classInfo.hInstance = s_windowInstance;
|
|
classInfo.hIcon = NULL;
|
|
classInfo.hCursor = NULL;
|
|
classInfo.hbrBackground = NULL;
|
|
classInfo.lpszMenuName = NULL;
|
|
classInfo.lpszClassName = "Barrier";
|
|
classInfo.hIconSm = NULL;
|
|
return RegisterClassEx(&classInfo);
|
|
}
|
|
|
|
void
|
|
MSWindowsScreen::destroyClass(ATOM windowClass) const
|
|
{
|
|
if (windowClass != 0) {
|
|
UnregisterClass(MAKEINTATOM(windowClass), s_windowInstance);
|
|
}
|
|
}
|
|
|
|
HWND
|
|
MSWindowsScreen::createWindow(ATOM windowClass, const char* name) const
|
|
{
|
|
HWND window = CreateWindowEx(WS_EX_TOPMOST |
|
|
WS_EX_TRANSPARENT |
|
|
WS_EX_TOOLWINDOW,
|
|
MAKEINTATOM(windowClass),
|
|
name,
|
|
WS_POPUP,
|
|
0, 0, 1, 1,
|
|
NULL, NULL,
|
|
s_windowInstance,
|
|
NULL);
|
|
if (window == NULL) {
|
|
LOG((CLOG_ERR "failed to create window: %d", GetLastError()));
|
|
throw XScreenOpenFailure();
|
|
}
|
|
return window;
|
|
}
|
|
|
|
HWND
|
|
MSWindowsScreen::createDropWindow(ATOM windowClass, const char* name) const
|
|
{
|
|
HWND window = CreateWindowEx(
|
|
WS_EX_TOPMOST |
|
|
WS_EX_TRANSPARENT |
|
|
WS_EX_ACCEPTFILES,
|
|
MAKEINTATOM(m_class),
|
|
name,
|
|
WS_POPUP,
|
|
0, 0, m_dropWindowSize, m_dropWindowSize,
|
|
NULL, NULL,
|
|
s_windowInstance,
|
|
NULL);
|
|
|
|
if (window == NULL) {
|
|
LOG((CLOG_ERR "failed to create drop window: %d", GetLastError()));
|
|
throw XScreenOpenFailure();
|
|
}
|
|
|
|
return window;
|
|
}
|
|
|
|
void
|
|
MSWindowsScreen::destroyWindow(HWND hwnd) const
|
|
{
|
|
if (hwnd != NULL) {
|
|
DestroyWindow(hwnd);
|
|
}
|
|
}
|
|
|
|
void
|
|
MSWindowsScreen::sendEvent(Event::Type type, void* data)
|
|
{
|
|
m_events->addEvent(Event(type, getEventTarget(), data));
|
|
}
|
|
|
|
void
|
|
MSWindowsScreen::sendClipboardEvent(Event::Type type, ClipboardID id)
|
|
{
|
|
ClipboardInfo* info = (ClipboardInfo*)malloc(sizeof(ClipboardInfo));
|
|
if (info == NULL) {
|
|
LOG((CLOG_ERR "malloc failed on %s:%s", __FILE__, __LINE__ ));
|
|
return;
|
|
}
|
|
info->m_id = id;
|
|
info->m_sequenceNumber = m_sequenceNumber;
|
|
sendEvent(type, info);
|
|
}
|
|
|
|
void
|
|
MSWindowsScreen::handleSystemEvent(const Event& event, void*)
|
|
{
|
|
MSG* msg = static_cast<MSG*>(event.getData());
|
|
assert(msg != NULL);
|
|
|
|
if (ArchMiscWindows::processDialog(msg)) {
|
|
return;
|
|
}
|
|
if (onPreDispatch(msg->hwnd, msg->message, msg->wParam, msg->lParam)) {
|
|
return;
|
|
}
|
|
TranslateMessage(msg);
|
|
DispatchMessage(msg);
|
|
}
|
|
|
|
void
|
|
MSWindowsScreen::updateButtons()
|
|
{
|
|
int numButtons = GetSystemMetrics(SM_CMOUSEBUTTONS);
|
|
m_buttons[kButtonNone] = false;
|
|
m_buttons[kButtonLeft] = (GetKeyState(VK_LBUTTON) < 0);
|
|
m_buttons[kButtonRight] = (GetKeyState(VK_RBUTTON) < 0);
|
|
m_buttons[kButtonMiddle] = (GetKeyState(VK_MBUTTON) < 0);
|
|
m_buttons[kButtonExtra0] = (numButtons >= 4) &&
|
|
(GetKeyState(VK_XBUTTON1) < 0);
|
|
m_buttons[kButtonExtra1] = (numButtons >= 5) &&
|
|
(GetKeyState(VK_XBUTTON2) < 0);
|
|
}
|
|
|
|
IKeyState*
|
|
MSWindowsScreen::getKeyState() const
|
|
{
|
|
return m_keyState;
|
|
}
|
|
|
|
bool
|
|
MSWindowsScreen::onPreDispatch(HWND hwnd,
|
|
UINT message, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
// handle event
|
|
switch (message) {
|
|
case BARRIER_MSG_SCREEN_SAVER:
|
|
return onScreensaver(wParam != 0);
|
|
|
|
case BARRIER_MSG_DEBUG:
|
|
LOG((CLOG_DEBUG1 "hook: 0x%08x 0x%08x", wParam, lParam));
|
|
return true;
|
|
}
|
|
|
|
if (m_isPrimary) {
|
|
return onPreDispatchPrimary(hwnd, message, wParam, lParam);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool
|
|
MSWindowsScreen::onPreDispatchPrimary(HWND,
|
|
UINT message, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
LOG((CLOG_DEBUG5 "handling pre-dispatch primary"));
|
|
|
|
// handle event
|
|
switch (message) {
|
|
case BARRIER_MSG_MARK:
|
|
return onMark(static_cast<UInt32>(wParam));
|
|
|
|
case BARRIER_MSG_KEY:
|
|
return onKey(wParam, lParam);
|
|
|
|
case BARRIER_MSG_MOUSE_BUTTON:
|
|
return onMouseButton(wParam, lParam);
|
|
|
|
case BARRIER_MSG_MOUSE_MOVE:
|
|
return onMouseMove(static_cast<SInt32>(wParam),
|
|
static_cast<SInt32>(lParam));
|
|
|
|
case BARRIER_MSG_MOUSE_WHEEL:
|
|
return onMouseWheel(static_cast<SInt32>(lParam), static_cast<SInt32>(wParam));
|
|
|
|
case BARRIER_MSG_PRE_WARP:
|
|
{
|
|
// save position to compute delta of next motion
|
|
saveMousePosition(static_cast<SInt32>(wParam), static_cast<SInt32>(lParam));
|
|
|
|
// we warped the mouse. discard events until we find the
|
|
// matching post warp event. see warpCursorNoFlush() for
|
|
// where the events are sent. we discard the matching
|
|
// post warp event and can be sure we've skipped the warp
|
|
// event.
|
|
MSG msg;
|
|
do {
|
|
GetMessage(&msg, NULL, BARRIER_MSG_MOUSE_MOVE,
|
|
BARRIER_MSG_POST_WARP);
|
|
} while (msg.message != BARRIER_MSG_POST_WARP);
|
|
}
|
|
return true;
|
|
|
|
case BARRIER_MSG_POST_WARP:
|
|
LOG((CLOG_WARN "unmatched post warp"));
|
|
return true;
|
|
|
|
case WM_HOTKEY:
|
|
// we discard these messages. we'll catch the hot key in the
|
|
// regular key event handling, where we can detect both key
|
|
// press and release. we only register the hot key so no other
|
|
// app will act on the key combination.
|
|
break;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool
|
|
MSWindowsScreen::onEvent(HWND, UINT msg,
|
|
WPARAM wParam, LPARAM lParam, LRESULT* result)
|
|
{
|
|
switch (msg) {
|
|
case WM_DRAWCLIPBOARD:
|
|
// first pass on the message
|
|
if (m_nextClipboardWindow != NULL) {
|
|
SendMessage(m_nextClipboardWindow, msg, wParam, lParam);
|
|
}
|
|
|
|
// now handle the message
|
|
return onClipboardChange();
|
|
|
|
case WM_CHANGECBCHAIN:
|
|
if (m_nextClipboardWindow == (HWND)wParam) {
|
|
m_nextClipboardWindow = (HWND)lParam;
|
|
LOG((CLOG_DEBUG "clipboard chain: new next: 0x%08x", m_nextClipboardWindow));
|
|
}
|
|
else if (m_nextClipboardWindow != NULL) {
|
|
SendMessage(m_nextClipboardWindow, msg, wParam, lParam);
|
|
}
|
|
return true;
|
|
|
|
case WM_DISPLAYCHANGE:
|
|
return onDisplayChange();
|
|
|
|
case WM_POWERBROADCAST:
|
|
switch (wParam) {
|
|
case PBT_APMRESUMEAUTOMATIC:
|
|
case PBT_APMRESUMECRITICAL:
|
|
case PBT_APMRESUMESUSPEND:
|
|
m_events->addEvent(Event(m_events->forIScreen().resume(),
|
|
getEventTarget(), NULL,
|
|
Event::kDeliverImmediately));
|
|
break;
|
|
|
|
case PBT_APMSUSPEND:
|
|
m_events->addEvent(Event(m_events->forIScreen().suspend(),
|
|
getEventTarget(), NULL,
|
|
Event::kDeliverImmediately));
|
|
break;
|
|
}
|
|
*result = TRUE;
|
|
return true;
|
|
|
|
case WM_DEVICECHANGE:
|
|
forceShowCursor();
|
|
break;
|
|
|
|
case WM_SETTINGCHANGE:
|
|
if (wParam == SPI_SETMOUSEKEYS) {
|
|
forceShowCursor();
|
|
}
|
|
break;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool
|
|
MSWindowsScreen::onMark(UInt32 mark)
|
|
{
|
|
m_markReceived = mark;
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
MSWindowsScreen::onKey(WPARAM wParam, LPARAM lParam)
|
|
{
|
|
static const KeyModifierMask s_ctrlAlt =
|
|
KeyModifierControl | KeyModifierAlt;
|
|
|
|
LOG((CLOG_DEBUG1 "event: Key char=%d, vk=0x%02x, nagr=%d, lParam=0x%08x", (wParam & 0xff00u) >> 8, wParam & 0xffu, (wParam & 0x10000u) ? 1 : 0, lParam));
|
|
|
|
// get event info
|
|
KeyButton button = (KeyButton)((lParam & 0x01ff0000) >> 16);
|
|
bool down = ((lParam & 0x80000000u) == 0x00000000u);
|
|
bool wasDown = isKeyDown(button);
|
|
KeyModifierMask oldState = pollActiveModifiers();
|
|
|
|
// check for autorepeat
|
|
if (m_keyState->testAutoRepeat(down, (lParam & 0x40000000u), button)) {
|
|
lParam |= 0x40000000u;
|
|
}
|
|
|
|
// if the button is zero then guess what the button should be.
|
|
// these are badly synthesized key events and logitech software
|
|
// that maps mouse buttons to keys is known to do this.
|
|
// alternatively, we could just throw these events out.
|
|
if (button == 0) {
|
|
button = m_keyState->virtualKeyToButton(wParam & 0xffu);
|
|
if (button == 0) {
|
|
return true;
|
|
}
|
|
wasDown = isKeyDown(button);
|
|
}
|
|
|
|
// record keyboard state
|
|
m_keyState->onKey(button, down, oldState);
|
|
|
|
if (!down && m_isPrimary && !m_isOnScreen) {
|
|
PrimaryKeyDownList::iterator find = std::find(m_primaryKeyDownList.begin(), m_primaryKeyDownList.end(), button);
|
|
if (find != m_primaryKeyDownList.end()) {
|
|
LOG((CLOG_DEBUG1 "release key button %d on primary", *find));
|
|
m_hook.setMode(kHOOK_WATCH_JUMP_ZONE);
|
|
fakeLocalKey(*find, false);
|
|
m_primaryKeyDownList.erase(find);
|
|
m_hook.setMode(kHOOK_RELAY_EVENTS);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// windows doesn't tell us the modifier key state on mouse or key
|
|
// events so we have to figure it out. most apps would use
|
|
// GetKeyState() or even GetAsyncKeyState() for that but we can't
|
|
// because our hook doesn't pass on key events for several modifiers.
|
|
// it can't otherwise the system would interpret them normally on
|
|
// the primary screen even when on a secondary screen. so tapping
|
|
// alt would activate menus and tapping the windows key would open
|
|
// the start menu. if you don't pass those events on in the hook
|
|
// then GetKeyState() understandably doesn't reflect the effect of
|
|
// the event. curiously, neither does GetAsyncKeyState(), which is
|
|
// surprising.
|
|
//
|
|
// so anyway, we have to track the modifier state ourselves for
|
|
// at least those modifiers we don't pass on. pollActiveModifiers()
|
|
// does that but we have to update the keyboard state before calling
|
|
// pollActiveModifiers() to get the right answer. but the only way
|
|
// to set the modifier state or to set the up/down state of a key
|
|
// is via onKey(). so we have to call onKey() twice.
|
|
KeyModifierMask state = pollActiveModifiers();
|
|
m_keyState->onKey(button, down, state);
|
|
|
|
// check for hot keys
|
|
if (oldState != state) {
|
|
// modifier key was pressed/released
|
|
if (onHotKey(0, lParam)) {
|
|
return true;
|
|
}
|
|
}
|
|
else {
|
|
// non-modifier was pressed/released
|
|
if (onHotKey(wParam, lParam)) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// stop sending modifier keys over and over again
|
|
if (isModifierRepeat(oldState, state, wParam)) {
|
|
return true;
|
|
}
|
|
|
|
// ignore message if posted prior to last mark change
|
|
if (!ignore()) {
|
|
// check for ctrl+alt+del. we do not want to pass that to the
|
|
// client. the user can use ctrl+alt+pause to emulate it.
|
|
UINT virtKey = (wParam & 0xffu);
|
|
if (virtKey == VK_DELETE && (state & s_ctrlAlt) == s_ctrlAlt) {
|
|
LOG((CLOG_DEBUG "discard ctrl+alt+del"));
|
|
return true;
|
|
}
|
|
|
|
// check for ctrl+alt+del emulation
|
|
if ((virtKey == VK_PAUSE || virtKey == VK_CANCEL) &&
|
|
(state & s_ctrlAlt) == s_ctrlAlt) {
|
|
LOG((CLOG_DEBUG "emulate ctrl+alt+del"));
|
|
// switch wParam and lParam to be as if VK_DELETE was
|
|
// pressed or released. when mapping the key we require that
|
|
// we not use AltGr (the 0x10000 flag in wParam) and we not
|
|
// use the keypad delete key (the 0x01000000 flag in lParam).
|
|
wParam = VK_DELETE | 0x00010000u;
|
|
lParam &= 0xfe000000;
|
|
lParam |= m_keyState->virtualKeyToButton(wParam & 0xffu) << 16;
|
|
lParam |= 0x01000001;
|
|
}
|
|
|
|
// process key
|
|
KeyModifierMask mask;
|
|
KeyID key = m_keyState->mapKeyFromEvent(wParam, lParam, &mask);
|
|
button = static_cast<KeyButton>((lParam & 0x01ff0000u) >> 16);
|
|
if (key != kKeyNone) {
|
|
// do it
|
|
m_keyState->sendKeyEvent(getEventTarget(),
|
|
((lParam & 0x80000000u) == 0),
|
|
((lParam & 0x40000000u) != 0),
|
|
key, mask, (SInt32)(lParam & 0xffff), button);
|
|
}
|
|
else {
|
|
LOG((CLOG_DEBUG1 "cannot map key"));
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
MSWindowsScreen::onHotKey(WPARAM wParam, LPARAM lParam)
|
|
{
|
|
// get the key info
|
|
KeyModifierMask state = getActiveModifiers();
|
|
UINT virtKey = (wParam & 0xffu);
|
|
UINT modifiers = 0;
|
|
if ((state & KeyModifierShift) != 0) {
|
|
modifiers |= MOD_SHIFT;
|
|
}
|
|
if ((state & KeyModifierControl) != 0) {
|
|
modifiers |= MOD_CONTROL;
|
|
}
|
|
if ((state & KeyModifierAlt) != 0) {
|
|
modifiers |= MOD_ALT;
|
|
}
|
|
if ((state & KeyModifierSuper) != 0) {
|
|
modifiers |= MOD_WIN;
|
|
}
|
|
|
|
// find the hot key id
|
|
HotKeyToIDMap::const_iterator i =
|
|
m_hotKeyToIDMap.find(HotKeyItem(virtKey, modifiers));
|
|
if (i == m_hotKeyToIDMap.end()) {
|
|
return false;
|
|
}
|
|
|
|
// find what kind of event
|
|
Event::Type type;
|
|
if ((lParam & 0x80000000u) == 0u) {
|
|
if ((lParam & 0x40000000u) != 0u) {
|
|
// ignore key repeats but it counts as a hot key
|
|
return true;
|
|
}
|
|
type = m_events->forIPrimaryScreen().hotKeyDown();
|
|
}
|
|
else {
|
|
type = m_events->forIPrimaryScreen().hotKeyUp();
|
|
}
|
|
|
|
// generate event
|
|
m_events->addEvent(Event(type, getEventTarget(),
|
|
HotKeyInfo::alloc(i->second)));
|
|
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
MSWindowsScreen::onMouseButton(WPARAM wParam, LPARAM lParam)
|
|
{
|
|
// get which button
|
|
bool pressed = mapPressFromEvent(wParam, lParam);
|
|
ButtonID button = mapButtonFromEvent(wParam, lParam);
|
|
|
|
// keep our shadow key state up to date
|
|
if (button >= kButtonLeft && button <= kButtonExtra0 + 1) {
|
|
if (pressed) {
|
|
m_buttons[button] = true;
|
|
if (button == kButtonLeft) {
|
|
m_draggingFilename.clear();
|
|
LOG((CLOG_DEBUG2 "dragging filename is cleared"));
|
|
}
|
|
}
|
|
else {
|
|
m_buttons[button] = false;
|
|
if (m_draggingStarted && button == kButtonLeft) {
|
|
m_draggingStarted = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
// ignore message if posted prior to last mark change
|
|
if (!ignore()) {
|
|
KeyModifierMask mask = m_keyState->getActiveModifiers();
|
|
if (pressed) {
|
|
LOG((CLOG_DEBUG1 "event: button press button=%d", button));
|
|
if (button != kButtonNone) {
|
|
sendEvent(m_events->forIPrimaryScreen().buttonDown(),
|
|
ButtonInfo::alloc(button, mask));
|
|
}
|
|
}
|
|
else {
|
|
LOG((CLOG_DEBUG1 "event: button release button=%d", button));
|
|
if (button != kButtonNone) {
|
|
sendEvent(m_events->forIPrimaryScreen().buttonUp(),
|
|
ButtonInfo::alloc(button, mask));
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// here's how mouse movements are sent across the network to a client:
|
|
// 1. barrier checks the mouse position on server screen
|
|
// 2. records the delta (current x,y minus last x,y)
|
|
// 3. records the current x,y as "last" (so we can calc delta next time)
|
|
// 4. on the server, puts the cursor back to the center of the screen
|
|
// - remember the cursor is hidden on the server at this point
|
|
// - this actually records the current x,y as "last" a second time (it seems)
|
|
// 5. sends the delta movement to the client (could be +1,+1 or -1,+4 for example)
|
|
bool
|
|
MSWindowsScreen::onMouseMove(SInt32 mx, SInt32 my)
|
|
{
|
|
// compute motion delta (relative to the last known
|
|
// mouse position)
|
|
SInt32 x = mx - m_xCursor;
|
|
SInt32 y = my - m_yCursor;
|
|
|
|
LOG((CLOG_DEBUG3
|
|
"mouse move - motion delta: %+d=(%+d - %+d),%+d=(%+d - %+d)",
|
|
x, mx, m_xCursor, y, my, m_yCursor));
|
|
|
|
// ignore if the mouse didn't move or if message posted prior
|
|
// to last mark change.
|
|
if (ignore() || (x == 0 && y == 0)) {
|
|
return true;
|
|
}
|
|
|
|
// save position to compute delta of next motion
|
|
saveMousePosition(mx, my);
|
|
|
|
if (m_isOnScreen) {
|
|
|
|
// motion on primary screen
|
|
sendEvent(
|
|
m_events->forIPrimaryScreen().motionOnPrimary(),
|
|
MotionInfo::alloc(m_xCursor, m_yCursor));
|
|
|
|
if (m_buttons[kButtonLeft] == true && m_draggingStarted == false) {
|
|
m_draggingStarted = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// the motion is on the secondary screen, so we warp mouse back to
|
|
// center on the server screen. if we don't do this, then the mouse
|
|
// will always try to return to the original entry point on the
|
|
// secondary screen.
|
|
LOG((CLOG_DEBUG5 "warping server cursor to center: %+d,%+d", m_xCenter, m_yCenter));
|
|
warpCursorNoFlush(m_xCenter, m_yCenter);
|
|
|
|
// examine the motion. if it's about the distance
|
|
// from the center of the screen to an edge then
|
|
// it's probably a bogus motion that we want to
|
|
// ignore (see warpCursorNoFlush() for a further
|
|
// description).
|
|
static SInt32 bogusZoneSize = 10;
|
|
if (-x + bogusZoneSize > m_xCenter - m_x ||
|
|
x + bogusZoneSize > m_x + m_w - m_xCenter ||
|
|
-y + bogusZoneSize > m_yCenter - m_y ||
|
|
y + bogusZoneSize > m_y + m_h - m_yCenter) {
|
|
|
|
LOG((CLOG_DEBUG "dropped bogus delta motion: %+d,%+d", x, y));
|
|
}
|
|
else {
|
|
// send motion
|
|
sendEvent(m_events->forIPrimaryScreen().motionOnSecondary(), MotionInfo::alloc(x, y));
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
MSWindowsScreen::onMouseWheel(SInt32 xDelta, SInt32 yDelta)
|
|
{
|
|
// ignore message if posted prior to last mark change
|
|
if (!ignore()) {
|
|
LOG((CLOG_DEBUG1 "event: button wheel delta=%+d,%+d", xDelta, yDelta));
|
|
sendEvent(m_events->forIPrimaryScreen().wheel(), WheelInfo::alloc(xDelta, yDelta));
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
MSWindowsScreen::onScreensaver(bool activated)
|
|
{
|
|
// ignore this message if there are any other screen saver
|
|
// messages already in the queue. this is important because
|
|
// our checkStarted() function has a deliberate delay, so it
|
|
// can't respond to events at full CPU speed and will fall
|
|
// behind if a lot of screen saver events are generated.
|
|
// that can easily happen because windows will continually
|
|
// send SC_SCREENSAVE until the screen saver starts, even if
|
|
// the screen saver is disabled!
|
|
MSG msg;
|
|
if (PeekMessage(&msg, NULL, BARRIER_MSG_SCREEN_SAVER,
|
|
BARRIER_MSG_SCREEN_SAVER, PM_NOREMOVE)) {
|
|
return true;
|
|
}
|
|
|
|
if (activated) {
|
|
if (!m_screensaverActive &&
|
|
m_screensaver->checkStarted(BARRIER_MSG_SCREEN_SAVER, FALSE, 0)) {
|
|
m_screensaverActive = true;
|
|
sendEvent(m_events->forIPrimaryScreen().screensaverActivated());
|
|
|
|
// enable display power down
|
|
ArchMiscWindows::removeBusyState(ArchMiscWindows::kDISPLAY);
|
|
}
|
|
}
|
|
else {
|
|
if (m_screensaverActive) {
|
|
m_screensaverActive = false;
|
|
sendEvent(m_events->forIPrimaryScreen().screensaverDeactivated());
|
|
|
|
// disable display power down
|
|
ArchMiscWindows::addBusyState(ArchMiscWindows::kDISPLAY);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
MSWindowsScreen::onDisplayChange()
|
|
{
|
|
// screen resolution may have changed. save old shape.
|
|
SInt32 xOld = m_x, yOld = m_y, wOld = m_w, hOld = m_h;
|
|
|
|
// update shape
|
|
updateScreenShape();
|
|
|
|
// do nothing if resolution hasn't changed
|
|
if (xOld != m_x || yOld != m_y || wOld != m_w || hOld != m_h) {
|
|
if (m_isPrimary) {
|
|
// warp mouse to center if off screen
|
|
if (!m_isOnScreen) {
|
|
|
|
LOG((CLOG_DEBUG1 "warping cursor to center: %+d, %+d", m_xCenter, m_yCenter));
|
|
warpCursor(m_xCenter, m_yCenter);
|
|
}
|
|
|
|
// tell hook about resize if on screen
|
|
else {
|
|
m_hook.setZone(m_x, m_y, m_w, m_h, getJumpZoneSize());
|
|
}
|
|
}
|
|
|
|
// send new screen info
|
|
sendEvent(m_events->forIScreen().shapeChanged());
|
|
|
|
LOG((CLOG_DEBUG "screen shape: %d,%d %dx%d %s", m_x, m_y, m_w, m_h, m_multimon ? "(multi-monitor)" : ""));
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
MSWindowsScreen::onClipboardChange()
|
|
{
|
|
// now notify client that somebody changed the clipboard (unless
|
|
// we're the owner).
|
|
if (!MSWindowsClipboard::isOwnedByBarrier()) {
|
|
if (m_ownClipboard) {
|
|
LOG((CLOG_DEBUG "clipboard changed: lost ownership"));
|
|
m_ownClipboard = false;
|
|
sendClipboardEvent(m_events->forClipboard().clipboardGrabbed(), kClipboardClipboard);
|
|
sendClipboardEvent(m_events->forClipboard().clipboardGrabbed(), kClipboardSelection);
|
|
}
|
|
}
|
|
else if (!m_ownClipboard) {
|
|
LOG((CLOG_DEBUG "clipboard changed: barrier owned"));
|
|
m_ownClipboard = true;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void
|
|
MSWindowsScreen::warpCursorNoFlush(SInt32 x, SInt32 y)
|
|
{
|
|
// send an event that we can recognize before the mouse warp
|
|
PostThreadMessage(GetCurrentThreadId(), BARRIER_MSG_PRE_WARP, x, y);
|
|
|
|
// warp mouse. hopefully this inserts a mouse motion event
|
|
// between the previous message and the following message.
|
|
SetCursorPos(x, y);
|
|
|
|
// check to see if the mouse pos was set correctly
|
|
POINT cursorPos;
|
|
GetCursorPos(&cursorPos);
|
|
|
|
// there is a bug or round error in SetCursorPos and GetCursorPos on
|
|
// a high DPI setting. The check here is for Vista/7 login screen.
|
|
// since this feature is mainly for client, so only check on client.
|
|
if (!isPrimary()) {
|
|
if ((cursorPos.x != x) && (cursorPos.y != y)) {
|
|
LOG((CLOG_DEBUG "SetCursorPos did not work; using fakeMouseMove instead"));
|
|
LOG((CLOG_DEBUG "cursor pos %d, %d expected pos %d, %d", cursorPos.x, cursorPos.y, x, y));
|
|
// when at Vista/7 login screen, SetCursorPos does not work (which could be
|
|
// an MS security feature). instead we can use fakeMouseMove, which calls
|
|
// mouse_event.
|
|
// IMPORTANT: as of implementing this function, it has an annoying side
|
|
// effect; instead of the mouse returning to the correct exit point, it
|
|
// returns to the center of the screen. this could have something to do with
|
|
// the center screen warping technique used (see comments for onMouseMove
|
|
// definition).
|
|
fakeMouseMove(x, y);
|
|
}
|
|
}
|
|
|
|
// yield the CPU. there's a race condition when warping:
|
|
// a hardware mouse event occurs
|
|
// the mouse hook is not called because that process doesn't have the CPU
|
|
// we send PRE_WARP, SetCursorPos(), send POST_WARP
|
|
// we process all of those events and update m_x, m_y
|
|
// we finish our time slice
|
|
// the hook is called
|
|
// the hook sends us a mouse event from the pre-warp position
|
|
// we get the CPU
|
|
// we compute a bogus warp
|
|
// we need the hook to process all mouse events that occur
|
|
// before we warp before we do the warp but i'm not sure how
|
|
// to guarantee that. yielding the CPU here may reduce the
|
|
// chance of undesired behavior. we'll also check for very
|
|
// large motions that look suspiciously like about half width
|
|
// or height of the screen.
|
|
ARCH->sleep(0.0);
|
|
|
|
// send an event that we can recognize after the mouse warp
|
|
PostThreadMessage(GetCurrentThreadId(), BARRIER_MSG_POST_WARP, 0, 0);
|
|
}
|
|
|
|
void
|
|
MSWindowsScreen::nextMark()
|
|
{
|
|
// next mark
|
|
++m_mark;
|
|
|
|
// mark point in message queue where the mark was changed
|
|
PostThreadMessage(GetCurrentThreadId(), BARRIER_MSG_MARK, m_mark, 0);
|
|
}
|
|
|
|
bool
|
|
MSWindowsScreen::ignore() const
|
|
{
|
|
return (m_mark != m_markReceived);
|
|
}
|
|
|
|
void
|
|
MSWindowsScreen::updateScreenShape()
|
|
{
|
|
// get shape and center
|
|
m_w = GetSystemMetrics(SM_CXVIRTUALSCREEN);
|
|
m_h = GetSystemMetrics(SM_CYVIRTUALSCREEN);
|
|
m_x = GetSystemMetrics(SM_XVIRTUALSCREEN);
|
|
m_y = GetSystemMetrics(SM_YVIRTUALSCREEN);
|
|
m_xCenter = GetSystemMetrics(SM_CXSCREEN) >> 1;
|
|
m_yCenter = GetSystemMetrics(SM_CYSCREEN) >> 1;
|
|
|
|
// check for multiple monitors
|
|
m_multimon = (m_w != GetSystemMetrics(SM_CXSCREEN) ||
|
|
m_h != GetSystemMetrics(SM_CYSCREEN));
|
|
|
|
// tell the desks
|
|
m_desks->setShape(m_x, m_y, m_w, m_h, m_xCenter, m_yCenter, m_multimon);
|
|
}
|
|
|
|
void
|
|
MSWindowsScreen::handleFixes(const Event&, void*)
|
|
{
|
|
// fix clipboard chain
|
|
fixClipboardViewer();
|
|
|
|
// update keys if keyboard layouts have changed
|
|
if (m_keyState->didGroupsChange()) {
|
|
updateKeys();
|
|
}
|
|
}
|
|
|
|
void
|
|
MSWindowsScreen::fixClipboardViewer()
|
|
{
|
|
// XXX -- disable this code for now. somehow it can cause an infinite
|
|
// recursion in the WM_DRAWCLIPBOARD handler. either we're sending
|
|
// the message to our own window or some window farther down the chain
|
|
// forwards the message to our window or a window farther up the chain.
|
|
// i'm not sure how that could happen. the m_nextClipboardWindow = NULL
|
|
// was not in the code that infinite loops and may fix the bug but i
|
|
// doubt it.
|
|
/*
|
|
ChangeClipboardChain(m_window, m_nextClipboardWindow);
|
|
m_nextClipboardWindow = NULL;
|
|
m_nextClipboardWindow = SetClipboardViewer(m_window);
|
|
*/
|
|
}
|
|
|
|
void
|
|
MSWindowsScreen::enableSpecialKeys(bool enable) const
|
|
{
|
|
}
|
|
|
|
ButtonID
|
|
MSWindowsScreen::mapButtonFromEvent(WPARAM msg, LPARAM button) const
|
|
{
|
|
switch (msg) {
|
|
case WM_LBUTTONDOWN:
|
|
case WM_LBUTTONDBLCLK:
|
|
case WM_LBUTTONUP:
|
|
case WM_NCLBUTTONDOWN:
|
|
case WM_NCLBUTTONDBLCLK:
|
|
case WM_NCLBUTTONUP:
|
|
return kButtonLeft;
|
|
|
|
case WM_MBUTTONDOWN:
|
|
case WM_MBUTTONDBLCLK:
|
|
case WM_MBUTTONUP:
|
|
case WM_NCMBUTTONDOWN:
|
|
case WM_NCMBUTTONDBLCLK:
|
|
case WM_NCMBUTTONUP:
|
|
return kButtonMiddle;
|
|
|
|
case WM_RBUTTONDOWN:
|
|
case WM_RBUTTONDBLCLK:
|
|
case WM_RBUTTONUP:
|
|
case WM_NCRBUTTONDOWN:
|
|
case WM_NCRBUTTONDBLCLK:
|
|
case WM_NCRBUTTONUP:
|
|
return kButtonRight;
|
|
|
|
case WM_XBUTTONDOWN:
|
|
case WM_XBUTTONDBLCLK:
|
|
case WM_XBUTTONUP:
|
|
case WM_NCXBUTTONDOWN:
|
|
case WM_NCXBUTTONDBLCLK:
|
|
case WM_NCXBUTTONUP:
|
|
switch (button) {
|
|
case XBUTTON1:
|
|
if (GetSystemMetrics(SM_CMOUSEBUTTONS) >= 4) {
|
|
return kButtonExtra0;
|
|
}
|
|
break;
|
|
|
|
case XBUTTON2:
|
|
if (GetSystemMetrics(SM_CMOUSEBUTTONS) >= 5) {
|
|
return kButtonExtra1;
|
|
}
|
|
break;
|
|
}
|
|
return kButtonNone;
|
|
|
|
default:
|
|
return kButtonNone;
|
|
}
|
|
}
|
|
|
|
bool
|
|
MSWindowsScreen::mapPressFromEvent(WPARAM msg, LPARAM) const
|
|
{
|
|
switch (msg) {
|
|
case WM_LBUTTONDOWN:
|
|
case WM_MBUTTONDOWN:
|
|
case WM_RBUTTONDOWN:
|
|
case WM_XBUTTONDOWN:
|
|
case WM_LBUTTONDBLCLK:
|
|
case WM_MBUTTONDBLCLK:
|
|
case WM_RBUTTONDBLCLK:
|
|
case WM_XBUTTONDBLCLK:
|
|
case WM_NCLBUTTONDOWN:
|
|
case WM_NCMBUTTONDOWN:
|
|
case WM_NCRBUTTONDOWN:
|
|
case WM_NCXBUTTONDOWN:
|
|
case WM_NCLBUTTONDBLCLK:
|
|
case WM_NCMBUTTONDBLCLK:
|
|
case WM_NCRBUTTONDBLCLK:
|
|
case WM_NCXBUTTONDBLCLK:
|
|
return true;
|
|
|
|
case WM_LBUTTONUP:
|
|
case WM_MBUTTONUP:
|
|
case WM_RBUTTONUP:
|
|
case WM_XBUTTONUP:
|
|
case WM_NCLBUTTONUP:
|
|
case WM_NCMBUTTONUP:
|
|
case WM_NCRBUTTONUP:
|
|
case WM_NCXBUTTONUP:
|
|
return false;
|
|
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void
|
|
MSWindowsScreen::updateKeysCB(void*)
|
|
{
|
|
// record which keys we think are down
|
|
bool down[IKeyState::kNumButtons];
|
|
bool sendFixes = (isPrimary() && !m_isOnScreen);
|
|
if (sendFixes) {
|
|
for (KeyButton i = 0; i < IKeyState::kNumButtons; ++i) {
|
|
down[i] = m_keyState->isKeyDown(i);
|
|
}
|
|
}
|
|
|
|
// update layouts if necessary
|
|
if (m_keyState->didGroupsChange()) {
|
|
PlatformScreen::updateKeyMap();
|
|
}
|
|
|
|
// now update the keyboard state
|
|
PlatformScreen::updateKeyState();
|
|
|
|
// now see which keys we thought were down but now think are up.
|
|
// send key releases for these keys to the active client.
|
|
if (sendFixes) {
|
|
KeyModifierMask mask = pollActiveModifiers();
|
|
for (KeyButton i = 0; i < IKeyState::kNumButtons; ++i) {
|
|
if (down[i] && !m_keyState->isKeyDown(i)) {
|
|
m_keyState->sendKeyEvent(getEventTarget(),
|
|
false, false, kKeyNone, mask, 1, i);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
MSWindowsScreen::forceShowCursor()
|
|
{
|
|
// check for mouse
|
|
m_hasMouse = (GetSystemMetrics(SM_MOUSEPRESENT) != 0);
|
|
|
|
// decide if we should show the mouse
|
|
bool showMouse = (!m_hasMouse && !m_isPrimary && m_isOnScreen);
|
|
|
|
// show/hide the mouse
|
|
if (showMouse != m_showingMouse) {
|
|
if (showMouse) {
|
|
m_oldMouseKeys.cbSize = sizeof(m_oldMouseKeys);
|
|
m_gotOldMouseKeys =
|
|
(SystemParametersInfo(SPI_GETMOUSEKEYS,
|
|
m_oldMouseKeys.cbSize, &m_oldMouseKeys, 0) != 0);
|
|
if (m_gotOldMouseKeys) {
|
|
m_mouseKeys = m_oldMouseKeys;
|
|
m_showingMouse = true;
|
|
updateForceShowCursor();
|
|
}
|
|
}
|
|
else {
|
|
if (m_gotOldMouseKeys) {
|
|
SystemParametersInfo(SPI_SETMOUSEKEYS,
|
|
m_oldMouseKeys.cbSize,
|
|
&m_oldMouseKeys, SPIF_SENDCHANGE);
|
|
m_showingMouse = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
MSWindowsScreen::updateForceShowCursor()
|
|
{
|
|
DWORD oldFlags = m_mouseKeys.dwFlags;
|
|
|
|
// turn on MouseKeys
|
|
m_mouseKeys.dwFlags = MKF_AVAILABLE | MKF_MOUSEKEYSON;
|
|
|
|
// make sure MouseKeys is active in whatever state the NumLock is
|
|
// not currently in.
|
|
if ((m_keyState->getActiveModifiers() & KeyModifierNumLock) != 0) {
|
|
m_mouseKeys.dwFlags |= MKF_REPLACENUMBERS;
|
|
}
|
|
|
|
// update MouseKeys
|
|
if (oldFlags != m_mouseKeys.dwFlags) {
|
|
SystemParametersInfo(SPI_SETMOUSEKEYS,
|
|
m_mouseKeys.cbSize, &m_mouseKeys, SPIF_SENDCHANGE);
|
|
}
|
|
}
|
|
|
|
LRESULT CALLBACK
|
|
MSWindowsScreen::wndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
assert(s_screen != NULL);
|
|
|
|
LRESULT result = 0;
|
|
if (!s_screen->onEvent(hwnd, msg, wParam, lParam, &result)) {
|
|
result = DefWindowProc(hwnd, msg, wParam, lParam);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
void
|
|
MSWindowsScreen::fakeLocalKey(KeyButton button, bool press) const
|
|
{
|
|
INPUT input;
|
|
input.type = INPUT_KEYBOARD;
|
|
input.ki.wVk = m_keyState->mapButtonToVirtualKey(button);
|
|
DWORD pressFlag = press ? KEYEVENTF_EXTENDEDKEY : KEYEVENTF_KEYUP;
|
|
input.ki.dwFlags = pressFlag;
|
|
input.ki.time = 0;
|
|
input.ki.dwExtraInfo = 0;
|
|
SendInput(1,&input,sizeof(input));
|
|
}
|
|
|
|
//
|
|
// MSWindowsScreen::HotKeyItem
|
|
//
|
|
|
|
MSWindowsScreen::HotKeyItem::HotKeyItem(UINT keycode, UINT mask) :
|
|
m_keycode(keycode),
|
|
m_mask(mask)
|
|
{
|
|
// do nothing
|
|
}
|
|
|
|
UINT
|
|
MSWindowsScreen::HotKeyItem::getVirtualKey() const
|
|
{
|
|
return m_keycode;
|
|
}
|
|
|
|
bool
|
|
MSWindowsScreen::HotKeyItem::operator<(const HotKeyItem& x) const
|
|
{
|
|
return (m_keycode < x.m_keycode ||
|
|
(m_keycode == x.m_keycode && m_mask < x.m_mask));
|
|
}
|
|
|
|
void
|
|
MSWindowsScreen::fakeDraggingFiles(DragFileList fileList)
|
|
{
|
|
// possible design flaw: this function stops a "not implemented"
|
|
// exception from being thrown.
|
|
}
|
|
|
|
std::string& MSWindowsScreen::getDraggingFilename()
|
|
{
|
|
if (m_draggingStarted) {
|
|
m_dropTarget->clearDraggingFilename();
|
|
m_draggingFilename.clear();
|
|
|
|
int halfSize = m_dropWindowSize / 2;
|
|
|
|
SInt32 xPos = m_isPrimary ? m_xCursor : m_xCenter;
|
|
SInt32 yPos = m_isPrimary ? m_yCursor : m_yCenter;
|
|
xPos = (xPos - halfSize) < 0 ? 0 : xPos - halfSize;
|
|
yPos = (yPos - halfSize) < 0 ? 0 : yPos - halfSize;
|
|
SetWindowPos(
|
|
m_dropWindow,
|
|
HWND_TOPMOST,
|
|
xPos,
|
|
yPos,
|
|
m_dropWindowSize,
|
|
m_dropWindowSize,
|
|
SWP_SHOWWINDOW);
|
|
|
|
// TODO: fake these keys properly
|
|
fakeKeyDown(kKeyEscape, 8192, 1);
|
|
fakeKeyUp(1);
|
|
fakeMouseButton(kButtonLeft, false);
|
|
|
|
std::string filename;
|
|
DOUBLE timeout = ARCH->time() + .5f;
|
|
while (ARCH->time() < timeout) {
|
|
ARCH->sleep(.05f);
|
|
filename = m_dropTarget->getDraggingFilename();
|
|
if (!filename.empty()) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
ShowWindow(m_dropWindow, SW_HIDE);
|
|
|
|
if (!filename.empty()) {
|
|
if (DragInformation::isFileValid(filename)) {
|
|
m_draggingFilename = filename;
|
|
}
|
|
else {
|
|
LOG((CLOG_DEBUG "drag file name is invalid: %s", filename.c_str()));
|
|
}
|
|
}
|
|
|
|
if (m_draggingFilename.empty()) {
|
|
LOG((CLOG_DEBUG "failed to get drag file name from OLE"));
|
|
}
|
|
}
|
|
|
|
return m_draggingFilename;
|
|
}
|
|
|
|
const std::string& MSWindowsScreen::getDropTarget() const
|
|
{
|
|
return m_desktopPath;
|
|
}
|
|
|
|
bool
|
|
MSWindowsScreen::isModifierRepeat(KeyModifierMask oldState, KeyModifierMask state, WPARAM wParam) const
|
|
{
|
|
bool result = false;
|
|
|
|
if (oldState == state && state != 0) {
|
|
UINT virtKey = (wParam & 0xffu);
|
|
if ((state & KeyModifierShift) != 0
|
|
&& (virtKey == VK_LSHIFT || virtKey == VK_RSHIFT)) {
|
|
result = true;
|
|
}
|
|
if ((state & KeyModifierControl) != 0
|
|
&& (virtKey == VK_LCONTROL || virtKey == VK_RCONTROL)) {
|
|
result = true;
|
|
}
|
|
if ((state & KeyModifierAlt) != 0
|
|
&& (virtKey == VK_LMENU || virtKey == VK_RMENU)) {
|
|
result = true;
|
|
}
|
|
if ((state & KeyModifierSuper) != 0
|
|
&& (virtKey == VK_LWIN || virtKey == VK_RWIN)) {
|
|
result = true;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|