mirror of
https://github.com/aseprite/aseprite.git
synced 2025-03-30 04:20:23 +00:00
Open files from Windows Explorer using DDE so we re-use the running instance
Fix #477
This commit is contained in:
parent
72338a6f26
commit
210ffc090e
@ -1,5 +1,5 @@
|
|||||||
# SHE
|
# SHE
|
||||||
# Copyright (C) 2012-2015 David Capello
|
# Copyright (C) 2012-2016 David Capello
|
||||||
|
|
||||||
set(SHE_SOURCES)
|
set(SHE_SOURCES)
|
||||||
|
|
||||||
@ -143,7 +143,8 @@ if(USE_SKIA_BACKEND)
|
|||||||
if(WIN32)
|
if(WIN32)
|
||||||
list(APPEND SHE_SOURCES
|
list(APPEND SHE_SOURCES
|
||||||
skia/skia_window_win.cpp
|
skia/skia_window_win.cpp
|
||||||
win/vk.cpp)
|
win/vk.cpp
|
||||||
|
win/window_dde.cpp)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(APPLE)
|
if(APPLE)
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
Copyright (c) 2012-2013 David Capello
|
Copyright (c) 2012-2016 David Capello
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
a copy of this software and associated documentation files (the
|
a copy of this software and associated documentation files (the
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
// SHE library
|
// SHE library
|
||||||
// Copyright (C) 2012-2015 David Capello
|
// Copyright (C) 2012-2016 David Capello
|
||||||
//
|
//
|
||||||
// This file is released under the terms of the MIT license.
|
// This file is released under the terms of the MIT license.
|
||||||
// Read LICENSE.txt for more information.
|
// Read LICENSE.txt for more information.
|
||||||
@ -82,6 +82,21 @@ bool capture_mouse = false;
|
|||||||
|
|
||||||
static LRESULT CALLBACK wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
|
static LRESULT CALLBACK wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
|
||||||
{
|
{
|
||||||
|
// Don't process DDE messages here because we cannot get the first
|
||||||
|
// DDE message correctly. The problem is that the message pump
|
||||||
|
// starts before we can subclass the Allegro HWND, so we don't have
|
||||||
|
// enough time to inject the code to process this kind of message.
|
||||||
|
//
|
||||||
|
// For more info see "Once you go input-idle, your application is
|
||||||
|
// deemed ready to receive DDE messages":
|
||||||
|
// https://blogs.msdn.microsoft.com/oldnewthing/20140620-00/?p=693
|
||||||
|
//
|
||||||
|
// Anyway a possible solution would be to avoid processing the
|
||||||
|
// message loop until we subclass the HWND. I've tested this and it
|
||||||
|
// doesn't work, maybe because the message pump on Allegro 4 isn't
|
||||||
|
// in the main thread, I really don't know. But it just crash the
|
||||||
|
// whole system (Windows 10).
|
||||||
|
|
||||||
switch (msg) {
|
switch (msg) {
|
||||||
|
|
||||||
case WM_DROPFILES: {
|
case WM_DROPFILES: {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
// SHE library
|
// SHE library
|
||||||
// Copyright (C) 2012-2015 David Capello
|
// Copyright (C) 2012-2016 David Capello
|
||||||
//
|
//
|
||||||
// This file is released under the terms of the MIT license.
|
// This file is released under the terms of the MIT license.
|
||||||
// Read LICENSE.txt for more information.
|
// Read LICENSE.txt for more information.
|
||||||
@ -22,6 +22,14 @@
|
|||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
#include "she/win/window_dde.h" // Include this one time to
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
namespace she {
|
namespace she {
|
||||||
|
|
||||||
SkiaWindow::SkiaWindow(EventQueue* queue, SkiaDisplay* display,
|
SkiaWindow::SkiaWindow(EventQueue* queue, SkiaDisplay* display,
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
// SHE library
|
// SHE library
|
||||||
// Copyright (C) 2012-2015 David Capello
|
// Copyright (C) 2012-2016 David Capello
|
||||||
//
|
//
|
||||||
// This file is released under the terms of the MIT license.
|
// This file is released under the terms of the MIT license.
|
||||||
// Read LICENSE.txt for more information.
|
// Read LICENSE.txt for more information.
|
||||||
@ -18,6 +18,7 @@
|
|||||||
#include "she/event.h"
|
#include "she/event.h"
|
||||||
#include "she/keys.h"
|
#include "she/keys.h"
|
||||||
#include "she/native_cursor.h"
|
#include "she/native_cursor.h"
|
||||||
|
#include "she/win/window_dde.h"
|
||||||
|
|
||||||
#ifndef WM_MOUSEHWHEEL
|
#ifndef WM_MOUSEHWHEEL
|
||||||
#define WM_MOUSEHWHEEL 0x020E
|
#define WM_MOUSEHWHEEL 0x020E
|
||||||
@ -554,6 +555,10 @@ namespace she {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LRESULT result = FALSE;
|
||||||
|
if (handle_dde_messages(m_hwnd, msg, wparam, lparam, result))
|
||||||
|
return result;
|
||||||
|
|
||||||
return DefWindowProc(m_hwnd, msg, wparam, lparam);
|
return DefWindowProc(m_hwnd, msg, wparam, lparam);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
205
src/she/win/window_dde.cpp
Normal file
205
src/she/win/window_dde.cpp
Normal file
@ -0,0 +1,205 @@
|
|||||||
|
// SHE library
|
||||||
|
// Copyright (C) 2016 David Capello
|
||||||
|
//
|
||||||
|
// This file is released under the terms of the MIT license.
|
||||||
|
// Read LICENSE.txt for more information.
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include "config.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "she/win/window_dde.h"
|
||||||
|
|
||||||
|
#include "base/string.h"
|
||||||
|
#include "she/event.h"
|
||||||
|
#include "she/event_queue.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace she {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
std::string get_atom_string(ATOM atom, bool isUnicode)
|
||||||
|
{
|
||||||
|
std::string result;
|
||||||
|
if (!atom)
|
||||||
|
return result;
|
||||||
|
|
||||||
|
if (isUnicode) {
|
||||||
|
std::vector<WCHAR> buf(256, 0);
|
||||||
|
GlobalGetAtomNameW(atom, &buf[0], buf.size());
|
||||||
|
result = base::to_utf8(std::wstring(&buf[0]));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
std::vector<char> buf;
|
||||||
|
UINT chars = GlobalGetAtomNameA(atom, &buf[0], buf.size());
|
||||||
|
result = &buf[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Converts a DDE command string (e.g. "[open(filename)]") into she events.
|
||||||
|
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms648995.aspx
|
||||||
|
bool parse_dde_command(const std::string& cmd)
|
||||||
|
{
|
||||||
|
bool result = false;
|
||||||
|
|
||||||
|
for (size_t i=0; i<cmd.size(); ) {
|
||||||
|
if (cmd[i] == '[') {
|
||||||
|
size_t j = ++i;
|
||||||
|
|
||||||
|
for (; j<cmd.size(); ++j) {
|
||||||
|
if (cmd[j] == '(')
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string cmdName = cmd.substr(i, j-i);
|
||||||
|
std::vector<std::string> cmdParams;
|
||||||
|
|
||||||
|
for (i=j+1; i<cmd.size(); ) {
|
||||||
|
if (cmd[i] == ')') {
|
||||||
|
j = i+1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (cmd[i] == '"') {
|
||||||
|
std::string cmdParam;
|
||||||
|
for (j=++i; j<cmd.size(); ++j) {
|
||||||
|
if (cmd[j] == '"')
|
||||||
|
break;
|
||||||
|
else
|
||||||
|
cmdParam.push_back(cmd[j]);
|
||||||
|
}
|
||||||
|
cmdParams.push_back(cmdParam);
|
||||||
|
i = j+1;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
std::string cmdParam;
|
||||||
|
for (j=i; j<cmd.size(); ++j) {
|
||||||
|
if (cmd[j] == ',' || cmd[j] == ')')
|
||||||
|
break;
|
||||||
|
else
|
||||||
|
cmdParam.push_back(cmd[j]);
|
||||||
|
}
|
||||||
|
cmdParams.push_back(cmdParam);
|
||||||
|
i = j;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cmdName == "open" && !cmdParams.empty()) {
|
||||||
|
// Send a message to open the file as if it were dropped into the window.
|
||||||
|
Event ev;
|
||||||
|
ev.setType(Event::DropFiles);
|
||||||
|
ev.setFiles(cmdParams);
|
||||||
|
she::queue_event(ev);
|
||||||
|
|
||||||
|
result = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
i = j;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
union DdeackContainer {
|
||||||
|
WORD value;
|
||||||
|
DDEACK ddeack;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // anonymous namespace
|
||||||
|
|
||||||
|
bool handle_dde_messages(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam, LRESULT& result)
|
||||||
|
{
|
||||||
|
switch (msg) {
|
||||||
|
|
||||||
|
case WM_DDE_INITIATE: {
|
||||||
|
HWND clienthwnd = (HWND)wparam;
|
||||||
|
UINT appAtom = 0, topicAtom = 0;
|
||||||
|
if (!UnpackDDElParam(msg, lparam,
|
||||||
|
&appAtom,
|
||||||
|
&topicAtom))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
bool useUnicode = (IsWindowUnicode(clienthwnd) ? true: false);
|
||||||
|
std::string app = get_atom_string((ATOM)appAtom, useUnicode);
|
||||||
|
std::string topic = get_atom_string((ATOM)topicAtom, useUnicode);
|
||||||
|
FreeDDElParam(msg, lparam);
|
||||||
|
|
||||||
|
if (app != PACKAGE) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We have to create new atoms for this WM_DDE_ACK, we cannot
|
||||||
|
// reuse them according to MSDN:
|
||||||
|
//
|
||||||
|
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms648996(v=vs.85).aspx
|
||||||
|
//
|
||||||
|
// Anyway it looks like Windows returns the same ATOM number.
|
||||||
|
ATOM newApp = (useUnicode ? GlobalAddAtomW(base::from_utf8(app).c_str()):
|
||||||
|
GlobalAddAtomA(app.c_str()));
|
||||||
|
ATOM newTopic = (useUnicode ? GlobalAddAtomW(base::from_utf8(topic).c_str()):
|
||||||
|
GlobalAddAtomA(topic.c_str()));
|
||||||
|
|
||||||
|
if (newApp && newTopic) {
|
||||||
|
SendMessage(clienthwnd,
|
||||||
|
WM_DDE_ACK,
|
||||||
|
(WPARAM)hwnd,
|
||||||
|
PackDDElParam(WM_DDE_ACK, newApp, newTopic));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newApp) GlobalDeleteAtom(newApp);
|
||||||
|
if (newTopic) GlobalDeleteAtom(newTopic);
|
||||||
|
|
||||||
|
result = 0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
case WM_DDE_EXECUTE: {
|
||||||
|
HWND clienthwnd = (HWND)wparam;
|
||||||
|
bool useUnicode = (IsWindowUnicode(clienthwnd) ? true: false);
|
||||||
|
LPCVOID cmdPtr = (LPCVOID)GlobalLock((HGLOBAL)lparam);
|
||||||
|
std::string cmd;
|
||||||
|
|
||||||
|
if (useUnicode)
|
||||||
|
cmd = base::to_utf8((const WCHAR*)cmdPtr);
|
||||||
|
else
|
||||||
|
cmd = (const CHAR*)cmdPtr;
|
||||||
|
|
||||||
|
GlobalUnlock((HGLOBAL)lparam);
|
||||||
|
|
||||||
|
DdeackContainer ddeack;
|
||||||
|
ddeack.value = 0;
|
||||||
|
ddeack.ddeack.fAck = 1;
|
||||||
|
|
||||||
|
PostMessage(clienthwnd, WM_DDE_ACK,
|
||||||
|
(WPARAM)hwnd,
|
||||||
|
ReuseDDElParam(lparam, msg, WM_DDE_ACK,
|
||||||
|
(UINT_PTR)ddeack.value,
|
||||||
|
(UINT_PTR)lparam));
|
||||||
|
|
||||||
|
if (parse_dde_command(cmd))
|
||||||
|
SetForegroundWindow(hwnd);
|
||||||
|
|
||||||
|
result = 0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
case WM_DDE_TERMINATE: {
|
||||||
|
HWND clienthwnd = (HWND)wparam;
|
||||||
|
PostMessage(clienthwnd, WM_DDE_TERMINATE, (WPARAM)hwnd, lparam);
|
||||||
|
result = 0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace she
|
19
src/she/win/window_dde.h
Normal file
19
src/she/win/window_dde.h
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
// SHE library
|
||||||
|
// Copyright (C) 2016 David Capello
|
||||||
|
//
|
||||||
|
// This file is released under the terms of the MIT license.
|
||||||
|
// Read LICENSE.txt for more information.
|
||||||
|
|
||||||
|
#ifndef SHE_WIN_WINDOW_DDE_H_INCLUDED
|
||||||
|
#define SHE_WIN_WINDOW_DDE_H_INCLUDED
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
namespace she {
|
||||||
|
|
||||||
|
bool handle_dde_messages(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam, LRESULT& result);
|
||||||
|
|
||||||
|
} // namespace she
|
||||||
|
|
||||||
|
#endif
|
Loading…
x
Reference in New Issue
Block a user