about summary refs log tree commit diff stats
path: root/305keyboard.subx
blob: 5c05be6ba1fce9fac02b0d42205dec37f55e22f7 (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
186
187
188
189
190
191
192
193
194
195
196
# Primitives for keyboard control.
# Require Linux and a modern terminal.

== code

enable-keyboard-immediate-mode:
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # . save registers
    50/push-eax
    51/push-ecx
    52/push-edx
    53/push-ebx
    56/push-esi
    57/push-edi
    #
    (_maybe-open-terminal)
    # var terminal-info/esi: (addr termios)
    # termios is a type from the Linux kernel. We don't care how large it is.
    81 5/subop/subtract %esp 0x100/imm32
    89/<- %esi 4/r32/esp
    # ioctl(*Terminal-file-descriptor, TCGETS, terminal-info)
    89/<- %edx 6/r32/esi
    b9/copy-to-ecx 0x5401/imm32/TCGETS
    8b/-> *Terminal-file-descriptor 3/r32/ebx
    e8/call syscall_ioctl/disp32
    # terminal-info->c_iflags &= Keyboard-immediate-mode-iflags
#?     (write-buffered Stderr "iflags before: ")
#?     (write-int32-hex-buffered Stderr *esi)
#?     (write-buffered Stderr Newline)
#?     (flush Stderr)
    8b/-> *esi 0/r32/eax  # Termios-c_iflag
    23/and *Keyboard-immediate-mode-iflags 0/r32/eax
    89/<- *esi 0/r32/eax  # Termios-c_iflag
#?     (write-buffered Stderr "iflags after: ")
#?     (write-int32-hex-buffered Stderr *esi)
#?     (write-buffered Stderr Newline)
#?     (flush Stderr)
    # terminal-info->c_lflags &= Keyboard-immediate-mode-lflags
#?     (write-buffered Stderr "lflags before: ")
#?     (write-int32-hex-buffered Stderr *(esi+0xc))
#?     (write-buffered Stderr Newline)
#?     (flush Stderr)
    8b/-> *(esi+0xc) 0/r32/eax  # Termios-c_lflag
    23/and *Keyboard-immediate-mode-lflags 0/r32/eax
    89/<- *(esi+0xc) 0/r32/eax  # Termios-c_lflag
#?     (write-buffered Stderr "lflags after: ")
#?     (write-int32-hex-buffered Stderr *(esi+0xc))
#?     (write-buffered Stderr Newline)
#?     (flush Stderr)
    # ioctl(*Terminal-file-descriptor, TCSETS, terminal-info)
    89/<- %edx 6/r32/esi
    b9/copy-to-ecx 0x5402/imm32/TCSETS
    8b/-> *Terminal-file-descriptor 3/r32/ebx
    e8/call syscall_ioctl/disp32
$enable-keyboard-immediate-mode:end:
    # . reclaim locals
    81 0/subop/add %esp 0x100/imm32
    # . restore registers
    5f/pop-to-edi
    5e/pop-to-esi
    5b/pop-to-ebx
    5a/pop-to-edx
    59/pop-to-ecx
    58/pop-to-eax
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

enable-keyboard-type-mode:
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # . save registers
    50/push-eax
    51/push-ecx
    52/push-edx
    53/push-ebx
    56/push-esi
    57/push-edi
    #
    (_maybe-open-terminal)
    # var terminal-info/esi: (addr termios)
    # termios is a type from the Linux kernel. We don't care how large it is.
    81 5/subop/subtract %esp 0x100/imm32
    89/<- %esi 4/r32/esp
    # ioctl(*Terminal-file-descriptor, TCGETS, terminal-info)
    89/<- %edx 6/r32/esi
    b9/copy-to-ecx 0x5401/imm32/TCGETS
    8b/-> *Terminal-file-descriptor 3/r32/ebx
    e8/call syscall_ioctl/disp32
    # terminal-info->c_iflags |= Keyboard-type-mode-iflags
    8b/-> *esi 0/r32/eax  # Termios-c_iflag
    0b/or *Keyboard-type-mode-iflags 0/r32/eax
    89/<- *esi 0/r32/eax  # Termios-c_iflag
    # terminal-info->c_lflags |= Keyboard-type-mode-lflags
    8b/-> *(esi+0xc) 0/r32/eax  # Termios-c_lflag
    0b/or *Keyboard-type-mode-lflags 0/r32/eax
    89/<- *(esi+0xc) 0/r32/eax  # Termios-c_lflag
    # ioctl(*Terminal-file-descriptor, TCSETS, terminal-info)
    89/<- %edx 6/r32/esi
    b9/copy-to-ecx 0x5402/imm32/TCSETS
    8b/-> *Terminal-file-descriptor 3/r32/ebx
    e8/call syscall_ioctl/disp32
$enable-keyboard-type-mode:end:
    # . reclaim locals
    81 0/subop/add %esp 0x100/imm32
    # . restore registers
    5f/pop-to-edi
    5e/pop-to-esi
    5b/pop-to-ebx
    5a/pop-to-edx
    59/pop-to-ecx
    58/pop-to-eax
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

read-key:  # -> result/eax: byte
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # . save registers
    51/push-ecx
    # var buf/ecx: (stream byte 1)
    68/push 0/imm32/data
    68/push 1/imm32/size  # 3 bytes of data unused
    68/push 0/imm32/read
    68/push 0/imm32/write
    89/<- %ecx 4/r32/esp
    #
    (read 2 %ecx)  # => eax
    8b/-> *(ecx+0xc) 0/r32/eax
$read-key:end:
    # . reclaim locals
    81 0/subop/add %esp 0x10/imm32
    # . restore registers
    59/pop-to-ecx
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

== data

# iflags:   octal     hex
#  IGNBRK  0000001   0x0001
#  BRKINT  0000002   0x0002
#  IGNPAR  0000004   0x0004
#  PARMRK  0000010   0x0008
#  INPCK   0000020   0x0010
#  ISTRIP  0000040   0x0020
#  INLCR   0000100   0x0040
#  IGNCR   0000200   0x0080
#  ICRNL   0000400   0x0100
#  IUCLC   0001000   0x0200
#  IXON    0002000   0x0400
#  IXANY   0004000   0x0800
#  IXOFF   0010000   0x1000
#  IMAXBEL 0020000   0x2000
#  IUTF8   0040000   0x4000

# lflags:
#  ISIG   0000001     0x0001
#  ICANON 0000002     0x0002
#  ECHO   0000010     0x0008
#  ECHOE  0000020     0x0010
#  ECHOK  0000040     0x0020
#  ECHONL 0000100     0x0040
#  NOFLSH 0000200     0x0080
#  TOSTOP 0000400     0x0100
#  IEXTEN 0100000     0x8000

# recipe for raw mode according to the termios.3 manpage on Linux:
#   termios_p->c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON);
#   termios_p->c_oflag &= ~OPOST;
#   termios_p->c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
#   termios_p->c_cflag &= ~(CSIZE | PARENB);
#   termios_p->c_cflag |= CS8;

Keyboard-immediate-mode-iflags:  # (addr tcflag_t)
#?     0xfffffa14  # ~IGNBRK & ~BRKINT & ~PARMRK & ~ISTRIP & ~INLCR & ~IGNCR & ~ICRNL & ~IXON
    0xffffffff/imm32

Keyboard-immediate-mode-lflags:  # (addr tcflag_t)
#?     0xffff7fb4/imm32  # ~ICANON & ~ISIG & ~IEXTEN & ~ECHO & ~ECHONL
    0xffffffb5/imm32  # ~ICANON & ~ECHO & ~ECHONL

Keyboard-type-mode-iflags:  # (addr tcflag_t)
    0x00000000/imm32  # ~Keyboard-immediate-mode-iflags

Keyboard-type-mode-lflags:  # (addr tcflag_t)
    0x0000004a/imm32  # ~Keyboard-immediate-mode-lflags
ed in each individual source file, and * distribute linked combinations including the two. * * You must obey the GNU General Public License in all respects for all of the * code used other than OpenSSL. If you modify file(s) with this exception, you * may extend this exception to your version of the file(s), but you are not * obligated to do so. If you do not wish to do so, delete this exception * statement from your version. If you delete this exception statement from all * source files in the program, then also delete it here. * */ #include "config.h" #include <assert.h> #include <stdlib.h> #include <string.h> #include <glib.h> #ifdef HAVE_LIBMESODE #include <mesode.h> #endif #ifdef HAVE_LIBSTROPHE #include <strophe.h> #endif #include "profanity.h" #include "log.h" #include "config/preferences.h" #include "plugins/plugins.h" #include "event/server_events.h" #include "event/client_events.h" #include "tools/autocomplete.h" #include "ui/ui.h" #include "xmpp/session.h" #include "xmpp/iq.h" #include "xmpp/connection.h" #include "xmpp/roster.h" #include "xmpp/roster_list.h" #include "xmpp/stanza.h" #include "xmpp/xmpp.h" // callback data for group commands typedef struct _group_data { char *name; char *group; } GroupData; // id handlers static int _group_add_id_handler(xmpp_stanza_t *const stanza, void *const userdata); static int _group_remove_id_handler(xmpp_stanza_t *const stanza, void *const userdata); static void _free_group_data(GroupData *data); void roster_request(void) { xmpp_ctx_t * const ctx = connection_get_ctx(); xmpp_stanza_t *iq = stanza_create_roster_iq(ctx); iq_send_stanza(iq); xmpp_stanza_release(iq); } void roster_send_add_new(const char *const barejid, const char *const name) { xmpp_ctx_t * const ctx = connection_get_ctx(); char *id = connection_create_stanza_id("roster"); xmpp_stanza_t *iq = stanza_create_roster_set(ctx, id, barejid, name, NULL); free(id); iq_send_stanza(iq); xmpp_stanza_release(iq); } void roster_send_remove(const char *const barejid) { xmpp_ctx_t * const ctx = connection_get_ctx(); xmpp_stanza_t *iq = stanza_create_roster_remove_set(ctx, barejid); iq_send_stanza(iq); xmpp_stanza_release(iq); } void roster_send_name_change(const char *const barejid, const char *const new_name, GSList *groups) { xmpp_ctx_t * const ctx = connection_get_ctx(); char *id = connection_create_stanza_id("roster"); xmpp_stanza_t *iq = stanza_create_roster_set(ctx, id, barejid, new_name, groups); free(id); iq_send_stanza(iq); xmpp_stanza_release(iq); } void roster_send_add_to_group(const char *const group, PContact contact) { GSList *groups = p_contact_groups(contact); GSList *new_groups = NULL; while (groups) { new_groups = g_slist_append(new_groups, strdup(groups->data)); groups = g_slist_next(groups); } new_groups = g_slist_append(new_groups, strdup(group)); // add an id handler to handle the response char *unique_id = connection_create_stanza_id(NULL); GroupData *data = malloc(sizeof(GroupData)); data->group = strdup(group); if (p_contact_name(contact)) { data->name = strdup(p_contact_name(contact)); } else { data->name = strdup(p_contact_barejid(contact)); } xmpp_ctx_t * const ctx = connection_get_ctx(); iq_id_handler_add(unique_id, _group_add_id_handler, (ProfIqFreeCallback)_free_group_data, data); xmpp_stanza_t *iq = stanza_create_roster_set(ctx, unique_id, p_contact_barejid(contact), p_contact_name(contact), new_groups); iq_send_stanza(iq); xmpp_stanza_release(iq); free(unique_id); } static int _group_add_id_handler(xmpp_stanza_t *const stanza, void *const userdata) { if (userdata) { GroupData *data = userdata; ui_group_added(data->name, data->group); _free_group_data(data); } return 0; } void roster_send_remove_from_group(const char *const group, PContact contact) { GSList *groups = p_contact_groups(contact); GSList *new_groups = NULL; while (groups) { if (strcmp(groups->data, group) != 0) { new_groups = g_slist_append(new_groups, strdup(groups->data)); } groups = g_slist_next(groups); } xmpp_ctx_t * const ctx = connection_get_ctx(); // add an id handler to handle the response char *unique_id = connection_create_stanza_id(NULL); GroupData *data = malloc(sizeof(GroupData)); data->group = strdup(group); if (p_contact_name(contact)) { data->name = strdup(p_contact_name(contact)); } else { data->name = strdup(p_contact_barejid(contact)); } iq_id_handler_add(unique_id, _group_remove_id_handler, (ProfIqFreeCallback)_free_group_data, data); xmpp_stanza_t *iq = stanza_create_roster_set(ctx, unique_id, p_contact_barejid(contact), p_contact_name(contact), new_groups); iq_send_stanza(iq); xmpp_stanza_release(iq); free(unique_id); } static int _group_remove_id_handler(xmpp_stanza_t *const stanza, void *const userdata) { if (userdata) { GroupData *data = userdata; ui_group_removed(data->name, data->group); free(data->name); free(data->group); free(userdata); } return 0; } void roster_set_handler(xmpp_stanza_t *const stanza) { xmpp_stanza_t *query = xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_QUERY); xmpp_stanza_t *item = xmpp_stanza_get_child_by_name(query, STANZA_NAME_ITEM); if (item == NULL) { return; } // if from attribute exists and it is not current users barejid, ignore push Jid *my_jid = jid_create(connection_get_fulljid()); const char *from = xmpp_stanza_get_from(stanza); if (from && (strcmp(from, my_jid->barejid) != 0)) { jid_destroy(my_jid); return; } jid_destroy(my_jid); const char *barejid = xmpp_stanza_get_attribute(item, STANZA_ATTR_JID); gchar *barejid_lower = g_utf8_strdown(barejid, -1); const char *name = xmpp_stanza_get_attribute(item, STANZA_ATTR_NAME); const char *sub = xmpp_stanza_get_attribute(item, STANZA_ATTR_SUBSCRIPTION); const char *ask = xmpp_stanza_get_attribute(item, STANZA_ATTR_ASK); // do not set nickname to empty string, set to NULL instead if (name && (strlen(name) == 0)) { name = NULL; } // remove from roster if (g_strcmp0(sub, "remove") == 0) { // remove barejid and name if (name == NULL) { name = barejid_lower; } roster_remove(name, barejid_lower); ui_roster_remove(barejid_lower); // otherwise update local roster } else { // check for pending out subscriptions gboolean pending_out = FALSE; if (ask && (strcmp(ask, "subscribe") == 0)) { pending_out = TRUE; } GSList *groups = roster_get_groups_from_item(item); // update the local roster PContact contact = roster_get_contact(barejid_lower); if (contact == NULL) { gboolean added = roster_add(barejid_lower, name, groups, sub, pending_out); if (added) { ui_roster_add(barejid_lower, name); } } else { sv_ev_roster_update(barejid_lower, name, groups, sub, pending_out); } } g_free(barejid_lower); return; } void roster_result_handler(xmpp_stanza_t *const stanza) { const char *id = xmpp_stanza_get_id(stanza); if (g_strcmp0(id, "roster") != 0) { return; } // handle initial roster response xmpp_stanza_t *query = xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_QUERY); xmpp_stanza_t *item = xmpp_stanza_get_children(query); while (item) { const char *barejid = xmpp_stanza_get_attribute(item, STANZA_ATTR_JID); gchar *barejid_lower = g_utf8_strdown(barejid, -1); const char *name = xmpp_stanza_get_attribute(item, STANZA_ATTR_NAME); const char *sub = xmpp_stanza_get_attribute(item, STANZA_ATTR_SUBSCRIPTION); // do not set nickname to empty string, set to NULL instead if (name && (strlen(name) == 0)) name = NULL; gboolean pending_out = FALSE; const char *ask = xmpp_stanza_get_attribute(item, STANZA_ATTR_ASK); if (g_strcmp0(ask, "subscribe") == 0) { pending_out = TRUE; } GSList *groups = roster_get_groups_from_item(item); gboolean added = roster_add(barejid_lower, name, groups, sub, pending_out); if (!added) { log_warning("Attempt to add contact twice: %s", barejid_lower); } g_free(barejid_lower); item = xmpp_stanza_get_next(item); } sv_ev_roster_received(); return; } GSList* roster_get_groups_from_item(xmpp_stanza_t *item) { GSList *groups = NULL; xmpp_stanza_t *group_element = xmpp_stanza_get_children(item); while (group_element) { if (strcmp(xmpp_stanza_get_name(group_element), STANZA_NAME_GROUP) == 0) { char *groupname = xmpp_stanza_get_text(group_element); if (groupname) { groups = g_slist_append(groups, groupname); } } group_element = xmpp_stanza_get_next(group_element); } return groups; } static void _free_group_data(GroupData *data) { if (data) { free(data->group); free(data->name); free(data); } }