aboutsummaryrefslogtreecommitdiff
path: root/harness/include/plugin.h
blob: c48a2f57f68aa9a482d54a48ee4de01fcb48acaa (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
#ifndef _PLUGIN_H_
#define _PLUGIN_H_

#include <dlfcn.h>
#include <linux/limits.h>
#include <pthread.h>
#include <stdint.h>
#include <stdlib.h>
#include <wlr/types/wlr_input_device.h>
#include <wlr/types/wlr_keyboard.h>

#include "plugin_types.h"
#include <foreign_intf.h>

/*
 * Marker macro to define what functions should be exported. This generates the
 * interface which the plugin needs to implement.
 */
#define EXPORT(a) a

#define EXPORT_INCLUDE(a)

// clang-format off
EXPORT_INCLUDE(<foreign_intf.h>)
EXPORT_INCLUDE(<wlr/types/wlr_keyboard.h>)
EXPORT_INCLUDE(<wlr/types/wlr_input_device.h>)
// clang-format on

#define MAX_QUEUED_ACTIONS 8

typedef void *dlhandle_t;

/* Opaque state for a plugin. Not to be touched by the harness (not that it
 * really can be.) */

struct PLUGIN;
/* This structure represents an action requested by the plugin for the harness.
 */
typedef struct {
  int (*action)(struct PLUGIN *requester, void *arg);
  void (*arg_dtor)(void *arg);
  union {
    void *ptr_arg;
    int int_arg;
    char *str_arg;
  };
} requested_action_t;

/*
 * Structure for the plugin.
 */
typedef struct PLUGIN {
  /* The argc this plugin is loaded with. Typically the argc from main(). */
  int argc;

  /* The argv this plugin is loaded with. Typically the argv from main(). */
  char **argv;

  /* Filename the plugin is loaded from. */
  char filename[PATH_MAX];

  /* Interface to the harness that this plugin can use. */
  foreign_interface_t foreign_intf;

  /* Opaque state of this plugin. The state is usually some kind of pointer to
   * the plugin state, but all the harness knows is the opaque state is a
   * pointer-sized piece of data.
   *
   * This opaque state is used in a linear pattern where the handlers take the
   * opaque state, maybe operate on it, and return a new opaque state, which is
   * then passed to the next handler, etc. It is on the plugin to properly
   * manager the memory for this state and to destroy it upon teardown.
   *
   * It's guaranteed that this state is used linearly, meaning the harness gives
   * up all ownership to it once passed into a handler. */
  opqst_t state;

  /* This plugin's lock. This avoids potential issues with multiple threads
   * trying to change the opaque state at once which can lead to undesireable
   * outcomes. */
  pthread_mutex_t lock;

  /** Set to not-zero if this plugin is initialized, otherwise set to zero. */
  int initialized;

  /* The handle to the shared library. */
  dlhandle_t library_handle;

  /* Pointer to the plugin name. This is in the shared library and a
   * null-terminated string. If the library does not have a plugin name, this
   * will be NULL. */
  const char *plugin_name;

  /**
   * Initializes the plugin on the first time, and only the first time, it is
   * loaded. This is used to do things like setup a runtime that cannot be
   * reliably torn down. It is up to the plugin to ensure this won't interfere
   * with hot-reloading.
   */
  EXPORT(void (*plugin_metaload)(int argc, char **argv));

  /** Intializes the plugin with the given argc/argv. This is the first thing
   * called on the plugin and is called immediately after the library is loaded.
   */
  EXPORT(void (*plugin_load)(int argc, char **argv, foreign_interface_t *intf));

  /* Start the plugin with the marshalled state from the previous plugin.
   *
   * This should return the opaque state from the mashalled_state.
   *
   * This function should not fail if the state cannot be demarshalled, rather a
   * default state should be returned. This is because changing the plugin and
   * hot-reloading can produce incompatibilities between the old state and the
   * new state, and this should not cause a failure.
   */
  EXPORT(opqst_t (*plugin_hot_start)(uint8_t *mashalled_state, uint32_t n));

  /*
   * Starts the plugin without a marshalled state. Happens during the first boot
   * when there is not state.
   */
  EXPORT(opqst_t (*plugin_cold_start)());

  /*
   * Marshals the state to a bytestring. The returned pointer should be malloc'd
   * on the heap. The harness takes ownership of the malloc'd pointer.
   *
   * This is usually called in preparation for a teardown followed by a
   * hot-start.
   */
  EXPORT(uint8_t *(*plugin_marshal_state)(opqst_t st, uint32_t *szout));

  /*
   * Teardown the plugin in preperation for the library's imminent unloading.
   */
  EXPORT(void (*plugin_teardown)(opqst_t));

  /*
   * Handles a keybinding.
   */
  EXPORT(opqst_t (*plugin_handle_keybinding)(
      struct wlr_keyboard *keyboard,
      struct wlr_keyboard_key_event *event, uint32_t modifiers, uint32_t keysym,
      uint32_t codepoint, int *out_handled, opqst_t state));

  /*
   * Handles a surface being mapped, unmapped or destroyed.
   */
  EXPORT(opqst_t (*plugin_handle_surface)(void *surface, surface_event_t event,
                                          opqst_t));

  /* List of requested actions by the plugin. Right now there is a maximum of 8
   * allowed at one time. That should be plenty. The actions should be flushed
   * after each call to a handler anyway. */
  size_t n_requested_actions;
  requested_action_t requested_actions[MAX_QUEUED_ACTIONS];
} plugin_t;

#undef EXPORT
#undef EXPORT_INCLUDE

/* Reloads the plugin. This tears down the existing plugin, marshals the state
 * for it and reloads it.
 *
 * This function will call dlclose on the plugin's library handle.
 */
int plugin_hot_reload(int argc, char **argv, const char *filepath,
                      plugin_t *plugin);

/*
 * Like hot-reload, but uses the same parameters the plugin was originally
 * loaded with.
 */
int plugin_hot_reload_same_state(plugin_t *plugin);

/* Starts a plugin in a cold state. Called after load_plugin_from_file. */
void plugin_cold_start(plugin_t *plugin);

/* Reads a plugin from a filename. */
int load_plugin_from_file(int argc, char **argv, const char *filename,
                          plugin_t *plugin);

void plugin_run_requested_actions(plugin_t *plugin);

#endif /* _PLUGIN_H_ */