Skip to content

Commit

Permalink
Add support for file drag 'n drop + clipboard interaction.
Browse files Browse the repository at this point in the history
  • Loading branch information
Themaister committed Nov 25, 2023
1 parent 28ac37c commit 8a74a67
Show file tree
Hide file tree
Showing 5 changed files with 132 additions and 3 deletions.
5 changes: 5 additions & 0 deletions application/application.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@ bool Application::init_platform(std::unique_ptr<WSIPlatform> new_platform)
#endif
platform = std::move(new_platform);
application_wsi.set_platform(platform.get());

if (auto *event = GRANITE_EVENT_MANAGER())
event->enqueue_latched<ApplicationWSIPlatformEvent>(*platform);

return true;
}

Expand All @@ -61,6 +65,7 @@ void Application::teardown_wsi()
{
event->dequeue_all_latched(DevicePipelineReadyEvent::get_type_id());
event->dequeue_all_latched(DeviceShaderModuleReadyEvent::get_type_id());
event->dequeue_all_latched(ApplicationWSIPlatformEvent::get_type_id());
}
application_wsi.teardown();
ready_modules = false;
Expand Down
53 changes: 53 additions & 0 deletions application/events/application_wsi_events.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ namespace Vulkan
{
class Device;
class ShaderManager;
class WSIPlatform;

class DeviceCreatedEvent : public Granite::Event
{
Expand Down Expand Up @@ -184,4 +185,56 @@ class SwapchainIndexEvent : public Granite::Event
Device &device;
unsigned index;
};

class ApplicationWSIPlatformEvent : public Granite::Event
{
public:
GRANITE_EVENT_TYPE_DECL(ApplicationWSIPlatformEvent)

explicit ApplicationWSIPlatformEvent(WSIPlatform &platform_)
: platform(platform_)
{}

WSIPlatform &get_platform() const
{
return platform;
}

private:
WSIPlatform &platform;
};

class ApplicationWindowFileDropEvent : public Granite::Event
{
public:
GRANITE_EVENT_TYPE_DECL(ApplicationWindowFileDropEvent)
explicit ApplicationWindowFileDropEvent(std::string path_)
: path(std::move(path_))
{}

const std::string &get_path() const
{
return path;
}

private:
std::string path;
};

class ApplicationWindowTextDropEvent : public Granite::Event
{
public:
GRANITE_EVENT_TYPE_DECL(ApplicationWindowTextDropEvent)
explicit ApplicationWindowTextDropEvent(std::string str_)
: str(std::move(str_))
{}

const std::string &get_text() const
{
return str;
}

private:
std::string str;
};
}
74 changes: 71 additions & 3 deletions application/platforms/application_sdl3.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,13 @@ struct WSIPlatformSDL : GraniteWSIPlatform
return &application.info;
}

void begin_drop_event() override
{
push_task_to_main_thread([]() {
SDL_SetEventEnabled(SDL_EVENT_DROP_FILE, SDL_TRUE);
});
}

void toggle_fullscreen()
{
bool is_fullscreen = (SDL_GetWindowFlags(window) & SDL_WINDOW_FULLSCREEN) != 0;
Expand Down Expand Up @@ -197,6 +204,7 @@ struct WSIPlatformSDL : GraniteWSIPlatform
std::lock_guard<std::mutex> holder{get_input_tracker().get_lock()};
flush_deferred_input_events();
process_events_async_thread();
process_events_async_thread_non_pollable();
return !request_tear_down.load();
}

Expand Down Expand Up @@ -405,6 +413,14 @@ struct WSIPlatformSDL : GraniteWSIPlatform
{
toggle_fullscreen();
}
else if (state == KeyState::Pressed && tolower(e.key.keysym.sym) == 'v' &&
(e.key.keysym.mod & SDL_KMOD_LCTRL) != 0)
{
push_non_pollable_task_to_async_thread([c = clipboard]() mutable {
if (auto *manager = GRANITE_EVENT_MANAGER())
manager->enqueue<Vulkan::ApplicationWindowTextDropEvent>(std::move(c));
});
}
else
{
Key key = sdl_key_to_granite_key(e.key.keysym.sym);
Expand All @@ -415,6 +431,37 @@ struct WSIPlatformSDL : GraniteWSIPlatform
}
break;

case SDL_EVENT_DROP_FILE:
if (e.drop.windowID == SDL_GetWindowID(window))
{
std::string str = e.drop.data;
push_non_pollable_task_to_async_thread([s = std::move(str)]() mutable {
if (auto *manager = GRANITE_EVENT_MANAGER())
manager->enqueue<Vulkan::ApplicationWindowFileDropEvent>(std::move(s));
});
}
break;

case SDL_EVENT_DROP_COMPLETE:
SDL_SetEventEnabled(SDL_EVENT_DROP_FILE, SDL_FALSE);
break;

case SDL_EVENT_CLIPBOARD_UPDATE:
if (SDL_HasClipboardText())
{
char *text = SDL_GetClipboardText();
if (text)
{
clipboard = text;
SDL_free(text);
}
else
clipboard.clear();
}
else
clipboard.clear();
break;

default:
break;
}
Expand Down Expand Up @@ -494,9 +541,6 @@ struct WSIPlatformSDL : GraniteWSIPlatform
void notify_close()
{
request_tear_down.store(true);
SDL_Event quit_event = {};
quit_event.type = SDL_EVENT_QUIT;
SDL_PushEvent(&quit_event);
}

#ifdef _WIN32
Expand All @@ -517,12 +561,19 @@ struct WSIPlatformSDL : GraniteWSIPlatform
push_task_to_list(task_list_async, std::forward<Op>(op));
}

template <typename Op>
void push_non_pollable_task_to_async_thread(Op &&op)
{
push_non_pollable_task_to_list(task_list_async, std::forward<Op>(op));
}

private:
SDL_Window *window = nullptr;
unsigned width = 0;
unsigned height = 0;
uint32_t wake_event_type = 0;
Options options;
std::string clipboard;

struct
{
Expand All @@ -536,6 +587,7 @@ struct WSIPlatformSDL : GraniteWSIPlatform
std::mutex lock;
std::condition_variable cond;
std::vector<std::function<void ()>> list;
std::vector<std::function<void ()>> non_pollable_list;
} task_list_main, task_list_async;

static void process_events_for_list(TaskList &list, bool blocking)
Expand All @@ -559,6 +611,14 @@ struct WSIPlatformSDL : GraniteWSIPlatform
list.cond.notify_one();
}

template <typename Op>
void push_non_pollable_task_to_list(TaskList &list, Op &&op)
{
std::lock_guard<std::mutex> holder{list.lock};
list.non_pollable_list.emplace_back(std::forward<Op>(op));
list.cond.notify_one();
}

void process_events_main_thread()
{
process_events_for_list(task_list_main, false);
Expand All @@ -574,6 +634,14 @@ struct WSIPlatformSDL : GraniteWSIPlatform
process_events_for_list(task_list_async, false);
}

void process_events_async_thread_non_pollable()
{
std::unique_lock<std::mutex> holder{task_list_async.lock};
for (auto &task : task_list_async.non_pollable_list)
task();
task_list_async.non_pollable_list.clear();
}

void process_events_async_thread_blocking()
{
process_events_for_list(task_list_async, true);
Expand Down
1 change: 1 addition & 0 deletions vulkan/wsi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1527,4 +1527,5 @@ void WSIPlatform::event_swapchain_created(Device *, VkSwapchainKHR, unsigned, un
void WSIPlatform::event_swapchain_destroyed() {}
void WSIPlatform::event_frame_tick(double, double) {}
void WSIPlatform::event_swapchain_index(Device *, unsigned) {}
void WSIPlatform::begin_drop_event() {}
}
2 changes: 2 additions & 0 deletions vulkan/wsi.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,8 @@ class WSIPlatform

virtual const VkApplicationInfo *get_application_info();

virtual void begin_drop_event();

protected:
unsigned current_swapchain_width = 0;
unsigned current_swapchain_height = 0;
Expand Down

0 comments on commit 8a74a67

Please sign in to comment.