A tiling window manager
at master 539 lines 13 kB view raw
1/* 2 * Copyright (C) 2000, 2001, 2002, 2003, 2004 Shawn Betts <sabetts@vcn.bc.ca> 3 * 4 * This program is free software; you can redistribute it and/or modify it 5 * under the terms of the GNU General Public License as published by the Free 6 * Software Foundation; either version 2 of the License, or (at your option) 7 * any later version. 8 * 9 * This program is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 12 * more details. 13 * 14 * You should have received a copy of the GNU General Public License along with 15 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple 16 * Place, Suite 330, Boston, MA 02111-1307 USA. 17 */ 18 19#include <ctype.h> 20#include <stdlib.h> 21#include <stdio.h> 22#include <string.h> 23#include <unistd.h> 24#include <X11/Xlib.h> 25#include <X11/keysym.h> 26#include <X11/Xutil.h> 27#include <X11/Xatom.h> 28 29#include "sdorfehs.h" 30 31/* bind functions */ 32static edit_status editor_forward_char(rp_input_line *line); 33static edit_status editor_backward_char(rp_input_line *line); 34static edit_status editor_forward_word(rp_input_line *line); 35static edit_status editor_backward_word(rp_input_line *line); 36static edit_status editor_beginning_of_line(rp_input_line *line); 37static edit_status editor_end_of_line(rp_input_line *line); 38static edit_status editor_delete_char(rp_input_line *line); 39static edit_status editor_backward_delete_char(rp_input_line *line); 40static edit_status editor_kill_word(rp_input_line *line); 41static edit_status editor_backward_kill_word(rp_input_line *line); 42static edit_status editor_kill_line(rp_input_line *line); 43static edit_status editor_paste_selection(rp_input_line *line); 44static edit_status editor_abort(rp_input_line *line); 45static edit_status editor_no_action(rp_input_line *line); 46static edit_status editor_enter(rp_input_line *line); 47static edit_status editor_history_previous(rp_input_line *line); 48static edit_status editor_history_next(rp_input_line *line); 49static edit_status editor_backward_kill_line(rp_input_line *line); 50static edit_status editor_complete_prev(rp_input_line *line); 51static edit_status editor_complete_next(rp_input_line *line); 52 53/* default edit action */ 54static edit_status editor_insert(rp_input_line *line, char *keysym_buf); 55 56static char *saved_command = NULL; 57 58typedef struct edit_binding edit_binding; 59 60struct edit_binding { 61 struct rp_key key; 62 edit_status(*func) (rp_input_line *); 63}; 64 65static edit_binding edit_bindings[] = 66 {{{XK_g, RP_CONTROL_MASK}, editor_abort}, 67 {{XK_Escape, 0}, editor_abort}, 68 {{XK_f, RP_CONTROL_MASK}, editor_forward_char}, 69 {{XK_Right, 0}, editor_forward_char}, 70 {{XK_b, RP_CONTROL_MASK}, editor_backward_char}, 71 {{XK_Left, 0}, editor_backward_char}, 72 {{XK_f, RP_META_MASK}, editor_forward_word}, 73 {{XK_b, RP_META_MASK}, editor_backward_word}, 74 {{XK_a, RP_CONTROL_MASK}, editor_beginning_of_line}, 75 {{XK_Home, 0}, editor_beginning_of_line}, 76 {{XK_e, RP_CONTROL_MASK}, editor_end_of_line}, 77 {{XK_End, 0}, editor_end_of_line}, 78 {{XK_d, RP_CONTROL_MASK}, editor_delete_char}, 79 {{XK_Delete, 0}, editor_delete_char}, 80 {{XK_BackSpace, 0}, editor_backward_delete_char}, 81 {{XK_h, RP_CONTROL_MASK}, editor_backward_delete_char}, 82 {{XK_BackSpace, RP_META_MASK}, editor_backward_kill_word}, 83 {{XK_d, RP_META_MASK}, editor_kill_word}, 84 {{XK_k, RP_CONTROL_MASK}, editor_kill_line}, 85 {{XK_u, RP_CONTROL_MASK}, editor_backward_kill_line}, 86 {{XK_y, RP_CONTROL_MASK}, editor_paste_selection}, 87 {{XK_p, RP_CONTROL_MASK}, editor_history_previous}, 88 {{XK_Up, 0}, editor_history_previous}, 89 {{XK_n, RP_CONTROL_MASK}, editor_history_next}, 90 {{XK_Down, 0}, editor_history_next}, 91 {{XK_Return, 0}, editor_enter}, 92 {{XK_m, RP_CONTROL_MASK}, editor_enter}, 93 {{XK_KP_Enter, 0}, editor_enter}, 94 {{XK_Tab, 0}, editor_complete_next}, 95 {{XK_ISO_Left_Tab, 0}, editor_complete_prev}, 96 {{0, 0}, 0}, 97}; 98 99rp_input_line * 100input_line_new(char *prompt, char *preinput, int history_id, 101 enum completion_styles style, completion_fn fn) 102{ 103 rp_input_line *line; 104 size_t length; 105 106 line = xmalloc(sizeof(rp_input_line)); 107 line->prompt = prompt; 108 line->compl = completions_new(fn, style); 109 line->history_id = history_id; 110 111 /* Allocate some memory to start with (100 extra bytes) */ 112 length = strlen(preinput); 113 line->size = length + 1 + 100; 114 line->buffer = xmalloc(line->size); 115 116 /* load in the preinput */ 117 memcpy(line->buffer, preinput, length); 118 line->buffer[length] = '\0'; 119 line->position = line->length = length; 120 121 return line; 122} 123 124void 125input_line_free(rp_input_line *line) 126{ 127 completions_free(line->compl); 128 free(line->buffer); 129 free(line); 130} 131 132edit_status 133execute_edit_action(rp_input_line *line, KeySym ch, unsigned int modifier, 134 char *keysym_buf) 135{ 136 struct edit_binding *binding = NULL; 137 int found_binding = 0; 138 edit_status status; 139 140 for (binding = edit_bindings; binding->func; binding++) { 141 if (ch == binding->key.sym && modifier == binding->key.state) { 142 found_binding = 1; 143 break; 144 } 145 } 146 147 if (found_binding) 148 status = binding->func(line); 149 else if (modifier && modifier != RP_SHIFT_MASK) 150 status = editor_no_action(line); 151 else 152 status = editor_insert(line, keysym_buf); 153 154 return status; 155} 156 157static edit_status 158editor_forward_char(rp_input_line *line) 159{ 160 if (line->position == line->length) 161 return EDIT_NO_OP; 162 163 if (isu8start(line->buffer[line->position])) { 164 do 165 line->position++; 166 while (isu8cont(line->buffer[line->position])); 167 } else 168 line->position++; 169 170 return EDIT_MOVE; 171} 172 173static edit_status 174editor_backward_char(rp_input_line *line) 175{ 176 if (line->position == 0) 177 return EDIT_NO_OP; 178 179 do { 180 line->position--; 181 } while (line->position > 0 && isu8cont(line->buffer[line->position])); 182 183 return EDIT_MOVE; 184} 185 186static edit_status 187editor_forward_word(rp_input_line *line) 188{ 189 if (line->position == line->length) 190 return EDIT_NO_OP; 191 192 while (line->position < line->length 193 && !isalnum((unsigned char) line->buffer[line->position])) 194 line->position++; 195 196 while (line->position < line->length 197 && (isalnum((unsigned char) line->buffer[line->position]) 198 || isu8char(line->buffer[line->position]))) 199 line->position++; 200 201 return EDIT_MOVE; 202} 203 204static edit_status 205editor_backward_word(rp_input_line *line) 206{ 207 if (line->position == 0) 208 return EDIT_NO_OP; 209 210 while (line->position > 0 && 211 !isalnum((unsigned char)line->buffer[line->position])) 212 line->position--; 213 214 while (line->position > 0 && 215 (isalnum((unsigned char)line->buffer[line->position]) 216 || isu8char(line->buffer[line->position]))) 217 line->position--; 218 219 return EDIT_MOVE; 220} 221 222static edit_status 223editor_beginning_of_line(rp_input_line *line) 224{ 225 if (line->position == 0) 226 return EDIT_NO_OP; 227 else { 228 line->position = 0; 229 return EDIT_MOVE; 230 } 231} 232 233static edit_status 234editor_end_of_line(rp_input_line *line) 235{ 236 if (line->position == line->length) 237 return EDIT_NO_OP; 238 else { 239 line->position = line->length; 240 return EDIT_MOVE; 241 } 242} 243 244static edit_status 245editor_delete_char(rp_input_line *line) 246{ 247 size_t diff = 0; 248 249 if (line->position == line->length) 250 return EDIT_NO_OP; 251 252 if (isu8start(line->buffer[line->position])) { 253 do { 254 diff++; 255 } while (isu8cont(line->buffer[line->position + diff])); 256 } else 257 diff++; 258 259 memmove(&line->buffer[line->position], 260 &line->buffer[line->position + diff], 261 line->length - line->position + diff + 1); 262 263 line->length -= diff; 264 265 return EDIT_DELETE; 266} 267 268static edit_status 269editor_backward_delete_char(rp_input_line *line) 270{ 271 size_t diff = 1; 272 273 if (line->position == 0) 274 return EDIT_NO_OP; 275 276 while (line->position - diff > 0 && 277 isu8cont(line->buffer[line->position - diff])) 278 diff++; 279 280 memmove(&line->buffer[line->position - diff], 281 &line->buffer[line->position], 282 line->length - line->position + 1); 283 284 line->position -= diff; 285 line->length -= diff; 286 287 return EDIT_DELETE; 288} 289 290static edit_status 291editor_kill_word(rp_input_line *line) 292{ 293 size_t diff = 0; 294 295 if (line->position == line->length) 296 return EDIT_NO_OP; 297 298 while (line->position + diff < line->length && 299 !isalnum((unsigned char) line->buffer[line->position + diff])) 300 diff++; 301 302 while (line->position + diff < line->length 303 && (isalnum((unsigned char) line->buffer[line->position + diff]) 304 || isu8char(line->buffer[line->position + diff]))) 305 diff++; 306 307 /* Add the word to the X11 selection. */ 308 set_nselection(&line->buffer[line->position], diff); 309 310 memmove(&line->buffer[line->position], 311 &line->buffer[line->position + diff], 312 line->length - line->position + diff + 1); 313 314 line->length -= diff; 315 316 return EDIT_DELETE; 317} 318 319static edit_status 320editor_backward_kill_word(rp_input_line *line) 321{ 322 size_t diff = 1; 323 324 if (line->position == 0) 325 return EDIT_NO_OP; 326 327 while (line->position - diff > 0 && 328 !isalnum((unsigned char) line->buffer[line->position - diff])) 329 diff++; 330 331 while (line->position - diff > 0 332 && (isalnum((unsigned char) line->buffer[line->position - diff]) 333 || isu8char(line->buffer[line->position - diff]))) 334 diff++; 335 336 /* Add the word to the X11 selection. */ 337 set_nselection(&line->buffer[line->position - diff], diff); 338 339 memmove(&line->buffer[line->position - diff], 340 &line->buffer[line->position], 341 line->length - line->position + 1); 342 343 line->position -= diff; 344 line->length -= diff; 345 346 return EDIT_DELETE; 347} 348 349static edit_status 350editor_kill_line(rp_input_line *line) 351{ 352 if (line->position == line->length) 353 return EDIT_NO_OP; 354 355 /* Add the line to the X11 selection. */ 356 set_selection(&line->buffer[line->position]); 357 358 line->length = line->position; 359 line->buffer[line->length] = '\0'; 360 361 return EDIT_DELETE; 362} 363 364/* Do the dirty work of killing a line backwards. */ 365static void 366backward_kill_line(rp_input_line *line) 367{ 368 memmove(&line->buffer[0], &line->buffer[line->position], 369 line->length - line->position + 1); 370 371 line->length -= line->position; 372 line->position = 0; 373} 374 375static edit_status 376editor_backward_kill_line(rp_input_line *line) 377{ 378 if (line->position == 0) 379 return EDIT_NO_OP; 380 381 /* Add the line to the X11 selection. */ 382 set_nselection(line->buffer, line->position); 383 384 backward_kill_line(line); 385 386 return EDIT_DELETE; 387} 388 389static edit_status 390editor_history_previous(rp_input_line *line) 391{ 392 const char *entry = history_previous(line->history_id); 393 394 if (entry) { 395 if (!saved_command) { 396 line->buffer[line->length] = '\0'; 397 saved_command = xstrdup(line->buffer); 398 PRINT_DEBUG(("saved current command line: \'%s\'\n", 399 saved_command)); 400 } 401 free(line->buffer); 402 line->buffer = xstrdup(entry); 403 line->length = strlen(line->buffer); 404 line->size = line->length + 1; 405 line->position = line->length; 406 PRINT_DEBUG(("entry: \'%s\'\n", line->buffer)); 407 } else { 408 PRINT_DEBUG(("- do nothing -\n")); 409 return EDIT_NO_OP; 410 } 411 412 return EDIT_INSERT; 413} 414 415static edit_status 416editor_history_next(rp_input_line *line) 417{ 418 const char *entry = history_next(line->history_id); 419 420 if (entry) { 421 free(line->buffer); 422 line->buffer = xstrdup(entry); 423 PRINT_DEBUG(("entry: \'%s\'\n", line->buffer)); 424 } else if (saved_command) { 425 free(line->buffer); 426 line->buffer = saved_command; 427 saved_command = NULL; 428 PRINT_DEBUG(("restored command line: \'%s\'\n", line->buffer)); 429 } else { 430 PRINT_DEBUG(("- do nothing -\n")); 431 return EDIT_NO_OP; 432 } 433 434 line->length = strlen(line->buffer); 435 line->size = line->length + 1; 436 line->position = line->length; 437 438 return EDIT_INSERT; 439} 440 441static edit_status 442editor_abort(rp_input_line *line) 443{ 444 return EDIT_ABORT; 445} 446 447static edit_status 448editor_no_action(rp_input_line *line) 449{ 450 return EDIT_NO_OP; 451} 452 453static edit_status 454editor_insert(rp_input_line *line, char *keysym_buf) 455{ 456 size_t nbytes; 457 458 PRINT_DEBUG(("keysym_buf: '%s'\n", keysym_buf)); 459 460 nbytes = strlen(keysym_buf); 461 if (line->length + nbytes > line->size - 1) { 462 line->size += nbytes + 100; 463 line->buffer = xrealloc(line->buffer, line->size); 464 } 465 memmove(&line->buffer[line->position + nbytes], 466 &line->buffer[line->position], 467 line->length - line->position + 1); 468 memcpy(&line->buffer[line->position], keysym_buf, nbytes); 469 470 line->length += nbytes; 471 line->position += nbytes; 472 473 return EDIT_INSERT; 474} 475 476static edit_status 477editor_enter(rp_input_line *line) 478{ 479 line->buffer[line->length] = '\0'; 480 history_add(line->history_id, line->buffer); 481 return EDIT_DONE; 482} 483 484static edit_status 485editor_paste_selection(rp_input_line *line) 486{ 487 char *text; 488 489 text = get_selection(); 490 if (text) { 491 editor_insert(line, text); 492 free(text); 493 return EDIT_INSERT; 494 } else 495 return EDIT_NO_OP; 496} 497 498static edit_status 499editor_complete(rp_input_line *line, int direction) 500{ 501 char *tmp; 502 char *s; 503 504 /* 505 * Create our partial string that will be used for completion. It is 506 * the characters up to the position of the cursor. 507 */ 508 tmp = xmalloc(line->position + 1); 509 memcpy(tmp, line->buffer, line->position); 510 tmp[line->position] = '\0'; 511 512 /* 513 * We don't need to free s because it's a string from the completion 514 * list. 515 */ 516 s = completions_complete(line->compl, tmp, direction); 517 free(tmp); 518 519 if (s == NULL) 520 return EDIT_NO_OP; 521 522 /* Insert the completion. */ 523 backward_kill_line(line); 524 editor_insert(line, s); 525 526 return EDIT_COMPLETE; 527} 528 529static edit_status 530editor_complete_next(rp_input_line *line) 531{ 532 return editor_complete(line, COMPLETION_NEXT); 533} 534 535static edit_status 536editor_complete_prev(rp_input_line *line) 537{ 538 return editor_complete(line, COMPLETION_PREVIOUS); 539}