File: chapter.md

package info (click to toggle)
erlang 1%3A27.3.4.1%2Bdfsg-1
  • links: PTS, VCS
  • area: main
  • in suites: trixie
  • size: 225,000 kB
  • sloc: erlang: 1,658,966; ansic: 405,769; cpp: 177,850; xml: 82,435; makefile: 15,031; sh: 14,401; lisp: 9,812; java: 8,603; asm: 6,541; perl: 5,836; python: 5,484; sed: 72
file content (234 lines) | stat: -rw-r--r-- 10,458 bytes parent folder | download | duplicates (2)
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
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
<!--
%CopyrightBegin%

Copyright Ericsson AB 2023-2024. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

%CopyrightEnd%
-->
# wx the erlang binding of wxWidgets

The _wx_ application is an erlang binding of _wxWidgets_. This document
describes the erlang mapping to wxWidgets and it's implementation. It is not a
complete users guide to wxWidgets. If you need that, you will have to read the
wxWidgets documentation instead. _wx_ tries to keep a one-to-one mapping with
the original API so that the original documentation and examples shall be as
easy as possible to use.

Wx examples and test suite can be found in the erlang src release. They
can also provide some help on how to use the API.

This is currently a very brief introduction to _wx_. The application is still
under development, which means the interface may change, and the test suite
currently have a poor coverage ratio.

## Contents

- [Introduction](chapter.md#introduction)
- [Multiple processes and memory handling](chapter.md#Multiple_processes_and_memory_handling)
- [Event Handling](chapter.md#Event_Handling)
- [Acknowledgments](chapter.md#acknowledgments)

## Introduction

The original _wxWidgets_ is an object-oriented (C++) API and that is reflected
in the erlang mapping. In most cases each class in wxWidgets is represented as a
module in erlang. This gives the _wx_ application a huge interface, spread over
several modules, and it all starts with the _wx_ module. The _wx_ module
contains functions to create and destroy the GUI, i.e. `wx:new/0`,
`wx:destroy/0`, and some other useful functions.

Objects or object references in _wx_ should be seen as erlang processes rather
than erlang terms. When you operate on them they can change state, e.g. they are
not functional objects as erlang terms are. Each object has a type or rather a
class, which is manipulated with the corresponding module or by sub-classes of
that object. Type checking is done so that a module only operates on it's
objects or inherited classes.

An object is created with _new_ and destroyed with _destroy_. Most functions in
the classes are named the same as their C++ counterpart, except that for
convenience, in erlang they start with a lowercase letter and the first argument
is the object reference. Optional arguments are last and expressed as tagged
tuples in any order.

For example the _wxWindow_ C++ class is implemented in the _wxWindow_ erlang
module and the member _wxWindow::CenterOnParent_ is thus
_wxWindow:centerOnParent_. The following C++ code:

```erlang
  wxWindow MyWin = new wxWindow();
  MyWin.CenterOnParent(wxVERTICAL);
  ...
  delete MyWin;
```

would in erlang look like:

```erlang
  MyWin = wxWindow:new(),
  wxWindow:centerOnParent(MyWin, [{dir,?wxVERTICAL}]),
  ...
  wxWindow:destroy(MyWin),
```

When you are reading wxWidgets documentation or the examples, you will notice
that some of the most basic classes are missing in _wx_, they are directly
mapped to corresponding erlang terms:

- **_wxPoint_ is represented by \{Xcoord,Ycoord\}**

- **_wxSize_ is represented by \{Width,Height\}**

- **_wxRect_ is represented by \{Xcoord,Ycoord,Width,Height\}**

- **_wxColour_ is represented by \{Red,Green,Blue\[,Alpha]\}**

- **_wxString_ is represented by
  [unicode:charlist()](`t:unicode:charlist/0`)**

- **_wxGBPosition_ is represented by \{Row,Column\}**

- **_wxGBSpan_ is represented by \{RowSpan,ColumnSPan\}**

- **_wxGridCellCoords_ is represented by \{Row,Column\}**

In the places where the erlang API differs from the original one it should be
obvious from the erlang documentation which representation has been used. E.g.
the C++ arrays and/or lists are sometimes represented as erlang lists and
sometimes as tuples.

Colours are represented with \{Red,Green,Blue\[,Alpha]\}, the Alpha value is
optional when used as an argument to functions, but it will always be returned
from _wx_ functions.

Defines, enumerations and global variables exists in `wx.hrl` as defines. Most
of these defines are constants but not all. Some are platform dependent and
therefore the global variables must be instantiated during runtime. These will
be acquired from the driver with a call, so not all defines can be used in
matching statements. Class local enumerations will be prefixed with the class
name and a underscore as in `ClassName_Enum`.

Additionally some global functions, i.e. non-class functions, exist in the
`wx_misc` module.

_Wx_ is implemented as a (threaded) driver and a rather direct interface
to the C++ API, with the drawback that if the erlang programmer does an error,
it might crash the emulator.

Since the driver is threaded it requires a _smp_ enabled emulator, that provides
a thread safe interface to the driver.

[](){: #Multiple_processes_and_memory_handling }

## Multiple processes and memory handling

The intention is that each erlang application calls wx:new() once to setup it's
GUI which creates an environment and a memory mapping. To be able to use _wx_
from several processes in your application, you must share the environment. You
can get the active environment with `wx:get_env/0` and set it in the new
processes with `wx:set_env/1`. Two processes or applications which have both
called wx:new() will not be able use each others objects.

```erlang
  wx:new(),
  MyWin = wxFrame:new(wx:null(), 42, "Example", []),
  Env = wx:get_env(),
  spawn(fun() ->
           wx:set_env(Env),
           %% Here you can do wx calls from your helper process.
           ...
        end),
  ...
```

When `wx:destroy/0` is invoked or when all processes in the application have
died, the memory is deleted and all windows created by that application are
closed.

The _wx_ application never cleans or garbage collects memory as long as the user
application is alive. Most of the objects are deleted when a window is closed,
or at least all the objects which have a parent argument that is non null. By
using `wxCLASS:destroy/1` when possible you can avoid an increasing memory
usage. This is especially important when _wxWidgets_ assumes or recommends that
you (or rather the C++ programmer) have allocated the object on the stack since
that will never be done in the erlang binding. For example `wxDC` class or its
sub-classes or `wxSizerFlags`.

Currently the dialogs show modal function freezes wxWidgets until the dialog is
closed. That is intended but in erlang where you can have several GUI
applications running at the same time it causes trouble. This will hopefully be
fixed in future _wxWidgets_ releases.

[](){: #Event_Handling }

## Event Handling

Event handling in _wx_ differs most from the original API. You must specify
every event you want to handle in _wxWidgets_, that is the same in the erlang
binding but you can choose to receive the events as messages or handle them with
callback _funs_.

Otherwise the event subscription is handled as _wxWidgets_ dynamic event-handler
connection. You subscribe to events of a certain type from objects with an _ID_
or within a range of *ID*s. The callback _fun_ is optional, if not supplied the
event will be sent to the process that called _connect/2_. Thus, a handler is a
callback _fun_ or a process which will receive an event message.

Events are handled in order from bottom to top, in the widgets hierarchy, by the
last subscribed handler first. Depending on if `wxEvent:skip()` is called the
event will be handled by the other handler(s) afterwards. Most of the events
have default event handler(s) installed.

Message events looks like
[\#wx\{id=integer(), obj=wx:wxObject(), userData=term(), event=Rec](`t:wxEvtHandler:wx/0`)
\}. The _id_ is the identifier of the object that received the event. The _obj_
field contains the object that you used _connect_ on. The _userData_ field
contains a user supplied term, this is an option to _connect_. And the _event_
field contains a record with event type dependent information. The first element
in the event record is always the type you subscribed to. For example if you
subscribed to _key_up_ events you will receive the `#wx{event=Event}` where
_Event_ will be a _wxKey_ event record where `Event#wxKey.type = key_up`.

In _wxWidgets_ the developer has to call `wxEvent:skip()` if he wants the event
to be processed by other handlers. You can do the same in _wx_ if you use
callbacks. If you want the event as messages you just don't supply a callback
and you can set the _skip_ option in _connect_ call to true or false, the
default it is false. True means that you get the message but let the subsequent
handlers also handle the event. If you want to change this behavior dynamically
you must use callbacks and call `wxEvent:skip()`.

Callback event handling is done by using the optional callback _fun/2_ when
attaching the handler. The _fun(#wx\{\},wxObject()_ must take two arguments
where the first is the same as with message events described above and the
second is an object reference to the actual event object. With the event object
you can call `wxEvent:skip()` and access all the data. When using callbacks you
must call `wxEvent:skip()` by yourself if you want any of the events to be
forwarded to the following handlers. The actual event objects are deleted after
the _fun_ returns.

The callbacks are always invoked by another process and have exclusive usage of
the GUI when invoked. This means that a callback _fun_ cannot use the process
dictionary and should not make calls to other processes. Calls to another
process inside a callback _fun_ may cause a deadlock if the other process is
waiting on completion of his call to the GUI.

## Acknowledgments

Mats-Ola Persson wrote the initial _wxWidgets_ binding as part of his master
thesis. The current version is a total re-write but many ideas have been reused.
The reason for the re-write was mostly due to the limited requirements he had
been given by us.

Also thanks to the _wxWidgets_ team that develops and supports it so we have
something to use.