/emscripten-browser-clipboard

Header-only C++ library providing easy browser clipboard access, for programs built with Emscripten.

Primary LanguageC++MIT LicenseMIT

Emscripten Browser Clipboard Library

Header-only C++ library providing easy browser clipboard access, for programs built with Emscripten. All code in a single header.

Intended for use with Emscripten code, this enables copy and paste functionality to and from the browser's clipboard, into your program.

There are two separate ways to copy data to the clipboard - a callback, or an instantaneous function - the right choice will depend on your use case.

This offers several advantages over existing solutions - it uses modern JS APIs, and there is no need to create text elements alongside your canvas on your page HTML, or make any other changes to the page content. Everything is handled by a single include in your C++ code.

Live demo

Try it out in your browser: https://armchair-software.github.io/emscripten-clipboard-demo/

Demo source: https://github.com/Armchair-Software/emscripten-clipboard-demo

Functionality

  • emscripten_browser_clipboard::paste()
  • emscripten_browser_clipboard::copy() (callback and instantaneous overloads)

Paste

The paste function sets a callback which handles paste events generated by the browser. The callback can be a free function, a member function using std::bind, or a lambda.

If you want your callback to communicate with your program, the function accepts an optional void pointer which is passed on to your callback. Use this to pass arbitrary data of your own to your callback, if needed.

Example

#include <emscripten_browser_clipboard.h>

// ...

  // set a lambda as a callback to handle paste data:
  emscripten_browser_clipboard::paste([](std::string const &paste_data, void *callback_data [[maybe_unused]]){
    std::cout << "Copied clipboard data: " << paste_data << std::endl;
  });

The paste call takes the following arguments:

  void paste(paste_handler callback,  // a callback to call with the paste data
             void *callback_data);    // optional data pointer to pass to your callback

The callback must have the following signature (defined as emscripten_browser_clipboard::paste_handler):

  void my_paste_handler(std::string const &paste_data,  // text content of the pasted data sent to the browser
                        void *callback_data);           // the data pointer you passed to the paste function

Copy

There are two ways of handling copy - by callback, or setting instantaneously. Each has important limitations to be aware of.

  • By callback. This allows you to set a callback, just like the paste function above, which activates when the browser generates a copy event. Your callback then returns the data that should be copied to the browser clipboard. The callback can be a free function, a member function using std::bind, or a lambda.
    • The limitation with this method is that browser events fire first, before your program has a chance to process any input - in that case, even though a browser event for a copy has fired, it may be difficult to determine what it is that is currently selected, that the user wants to copy. This is the case with ImGui (see below) - in that case it's better to prefer the instantaneous method.
  • Instantaneously. This allows you to (attempt to) set clipboard data at will in your program, for example in response to a user-generated event in your GUI. The function is simply called with a string containing the data you wish to set. It uses the async clipboard API, and returns immediately.
    • A limitation with this method is that some browsers may block the attempt unless there is a corresponding user-generated copy event registered at the same time, usually if the user presses ctrl-C or similar. Without it, you may be unable to set system clipboard data arbitrarily (so, clicking on a "copy to clipboard" button may not work, and you may need to require your users to press ctrl-C to copy).

It is usually best to prefer the callback method, unless you have your own GUI implementing clipboard operations that you wish to tie in with. For more on this, see the ImGui example below.

Example: callback

#include <emscripten_browser_clipboard.h>

// ...

  std::string my_content{"This is something for the clipboard."};

  // set a lambda as a callback to handle copy events:
  emscripten_browser_clipboard::copy([](void *callback_data [[maybe_unused]]){
    return my_content.c_str();
  });
  

Example: instantaneous

#include <emscripten_browser_clipboard.h>

// ...

  std::string my_content{"This is something for the clipboard."};

  emscripten_browser_clipboard::copy(my_content); // attempt to set clipboard content immediately

Use with ImGui

Following is a simplified example of using Emscripten Browser Clipboard with ImGui. If you already have an application using ImGui text inputs, the following is all that is needed to enable seamless copy and paste operation between the browser (and hence the user's system) and your application, for those text input fields:

#include <emscripten_browser_clipboard.h>
#include <imgui/imgui.h>
#include <iostream>

std::string content;  // this stores the content for our internal clipboard

char const *get_content_for_imgui(void *user_data [[maybe_unused]]) {
  /// Callback for imgui, to return clipboard content
  std::cout << "ImGui requested clipboard content, returning " << std::quoted(content) << std::endl;
  return content.c_str();
}

void set_content_from_imgui(void *user_data [[maybe_unused]], char const *text) {
  /// Callback for imgui, to set clipboard content
  content = text;
  std::cout << "ImGui setting clipboard content to " << std::quoted(content) << std::endl;
  emscripten_browser_clipboard::copy(content);  // send clipboard data to the browser
}

// ...

  emscripten_browser_clipboard::paste([](std::string const &paste_data, void *callback_data [[maybe_unused]]){
    /// Callback to handle clipboard paste from browser
    std::cout << "Clipboard updated from paste data: " << std::quoted(paste_data) << std::endl;
    content = std::move(paste_data);
  });
  
  // set ImGui callbacks for clipboard access:
  ImGuiIO &imgui_io = ImGui::GetIO();
  imgui_io.GetClipboardTextFn = get_content_for_imgui;
  imgui_io.SetClipboardTextFn = set_content_from_imgui;

Limitations / future expansion

At the time of writing, this only handles copying and pasting plain text. It can be easily extended to handle arbitrary data - if I don't get round to doing this soon, please feel free to submit a pull request.

The async copy implementation currently ignores the result of the async operation, which may fail for various reasons. The library could be extended to call on_success / on_failure callbacks as a consequence of the operation, if desired.

Other useful libraries

You may also find the following Emscripten helper libraries useful: