mirror of
https://github.com/aseprite/aseprite.git
synced 2025-03-18 07:21:09 +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
|
||||
# Copyright (C) 2012-2015 David Capello
|
||||
# Copyright (C) 2012-2016 David Capello
|
||||
|
||||
set(SHE_SOURCES)
|
||||
|
||||
@ -143,7 +143,8 @@ if(USE_SKIA_BACKEND)
|
||||
if(WIN32)
|
||||
list(APPEND SHE_SOURCES
|
||||
skia/skia_window_win.cpp
|
||||
win/vk.cpp)
|
||||
win/vk.cpp
|
||||
win/window_dde.cpp)
|
||||
endif()
|
||||
|
||||
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
|
||||
a copy of this software and associated documentation files (the
|
||||
|
@ -1,5 +1,5 @@
|
||||
// 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.
|
||||
// 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)
|
||||
{
|
||||
// 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) {
|
||||
|
||||
case WM_DROPFILES: {
|
||||
|
@ -1,5 +1,5 @@
|
||||
// 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.
|
||||
// Read LICENSE.txt for more information.
|
||||
@ -22,6 +22,14 @@
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
#include <windows.h>
|
||||
#include "she/win/window_dde.h" // Include this one time to
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
namespace she {
|
||||
|
||||
SkiaWindow::SkiaWindow(EventQueue* queue, SkiaDisplay* display,
|
||||
|
@ -1,5 +1,5 @@
|
||||
// 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.
|
||||
// Read LICENSE.txt for more information.
|
||||
@ -18,6 +18,7 @@
|
||||
#include "she/event.h"
|
||||
#include "she/keys.h"
|
||||
#include "she/native_cursor.h"
|
||||
#include "she/win/window_dde.h"
|
||||
|
||||
#ifndef WM_MOUSEHWHEEL
|
||||
#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);
|
||||
}
|
||||
|
||||
|
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