1 module unde.command_line.lib; 2 3 import unde.global_state; 4 import unde.path_mnt; 5 import unde.lib; 6 import unde.slash; 7 import unde.font; 8 import unde.keybar.lib; 9 import unde.command_line.db; 10 import unde.command_line.run; 11 import unde.command_line.delete_command; 12 13 import berkeleydb.all; 14 15 import derelict.sdl2.sdl; 16 import derelict.sdl2.ttf; 17 18 import core.exception; 19 20 import std..string; 21 import std.stdio; 22 import std.math; 23 import std.conv; 24 import std.utf; 25 import std.file; 26 import std.path; 27 import std.process; 28 import std.concurrency; 29 import std.algorithm.iteration; 30 import std.algorithm.sorting; 31 import core.sys.posix.signal; 32 import core.sys.posix.stdlib; 33 34 public ssize_t 35 get_position_by_chars( 36 int x, int y, SDL_Rect[] chars, ssize_t p=0) 37 { 38 if (chars.length == 0) return -1; 39 ssize_t pos = chars.length/2; 40 while (pos >= 0 && chars[pos].w == 0 && chars[pos].h == 0) 41 pos--; 42 if (pos < 0) return -1; 43 44 if (y < chars[pos].y) 45 { 46 return get_position_by_chars(x, y, chars[0..pos], p); 47 } 48 else if (y > chars[pos].y + chars[pos].h) 49 { 50 return get_position_by_chars(x, y, chars[pos+1..$], p+pos+1); 51 } 52 else if (x < chars[pos].x) 53 { 54 return get_position_by_chars(x, y, chars[0..pos], p); 55 } 56 else if (x > chars[pos].x + chars[pos].w) 57 { 58 return get_position_by_chars(x, y, chars[pos+1..$], p+pos+1); 59 } 60 else 61 { 62 return p+pos; 63 } 64 } 65 66 private void 67 fix_bottom_line(GlobalState gs) 68 { 69 //writefln("Fix Bottom Line"); 70 with (gs.command_line) 71 { 72 nav_cmd_id = 0; 73 nav_out_id = 0; 74 nav_skip_cmd_id = 0; 75 76 cwd = gs.current_path; 77 78 Dbc cursor = gs.db_commands.cursor(null, 0); 79 scope(exit) cursor.close(); 80 81 Dbt key, data; 82 83 ulong id = mouse.cmd_id > 0 ? mouse.cmd_id : 1; 84 string ks = get_key_for_command(command_key(cwd, id)); 85 //writefln("SET RANGE: %s (cwd=%s, id=%X)", ks, cwd, id); 86 key = ks; 87 id = find_next_by_key(cursor, 0, id, key, data); 88 89 //writefln("mouse.cmd_id=%s", mouse.cmd_id); 90 if (id != 0) 91 { 92 bool cmd_next_stop; 93 /* The bottom command found */ 94 long y_off = neg_y; 95 //writefln("y=%s, y_off=%s", y, y_off); 96 do 97 { 98 string key_string = key.to!(string); 99 command_key cmd_key; 100 parse_key_for_command(key_string, cmd_key); 101 if (cwd != cmd_key.cwd) 102 { 103 id = 0; 104 break; 105 } 106 else 107 { 108 //writefln("cmd_id=%s", id); 109 id = cmd_key.id; 110 string data_string = data.to!(string); 111 command_data cmd_data; 112 parse_data_for_command(data_string, cmd_data); 113 114 if (search_mode && cmd_data.command.indexOf(search) < 0) 115 continue; 116 117 if (cmd_next_stop) 118 { 119 cmd_next_stop = false; 120 nav_skip_cmd_id = cmd_key.id; 121 //writefln("nav_skip_cmd_id=%d", nav_skip_cmd_id); 122 if (fontsize < 5) 123 { 124 int line_height9 = cast(int)(round(SQRT2^^fontsize)*1.2); 125 y = y_off - gs.screen.h + line_height9*3 + 8; 126 //writefln("CMD y = %d", y); 127 } 128 break; 129 } 130 131 int line_height = cast(int)(round(SQRT2^^9)*1.2); 132 auto rt = gs.text_viewer.font.get_size_of_line(cmd_data.command, 133 9, gs.screen.w-80, line_height, SDL_Color(0xFF,0x00,0xFF,0xFF)); 134 135 int lines = rt.h / line_height; 136 137 auto rect = SDL_Rect(); 138 rect.x = 40; 139 rect.y = cast(int)(y_off + 4); 140 rect.w = rt.w; 141 rect.h = rt.h; 142 143 if (cmd_key.id == mouse.cmd_id && 144 (0 == mouse.out_id || fontsize < 5)) 145 { 146 int ry = cast(int)(gs.mouse_screen_y - mouse_rel_y*rect.h); 147 y_off = ry - 4 + neg_y; 148 //writefln("on mouse cmd_id=%d ry-4=%d y_off=%s", mouse.cmd_id, ry-4, y_off); 149 } 150 151 //writefln("CMD id=%s %s, rect.y = %s > %s", cmd_key.id, cmd_data.command, rect.y, gs.screen.h + line_height); 152 if (rect.y >= gs.screen.h + line_height && nav_skip_cmd_id == 0) 153 { 154 cmd_next_stop = true; 155 } 156 if (nav_skip_cmd_id > 0 && (nav_cmd_id > 0 || fontsize < 5) ) 157 break; 158 159 y_off += line_height*lines; 160 161 /* Loop through all commands */ 162 if (fontsize >= 5 && !search_mode) 163 { 164 /* Try to find last output for command */ 165 Dbc cursor2 = gs.db_command_output.cursor(null, 0); 166 scope(exit) cursor2.close(); 167 168 Dbt key2, data2; 169 ulong out_id = cmd_key.id == mouse.cmd_id && mouse.out_id > 0 ? mouse.out_id : 1; 170 ks = get_key_for_command_out(command_out_key(cwd, cmd_key.id, out_id)); 171 //writefln("SET RANGE: %s (cwd=%s, id=%X)", ks, cwd, id); 172 key2 = ks; 173 out_id = find_next_by_key(cursor2, 0, out_id, key2, data2); 174 175 //writefln("mouse.out_id=%s", mouse.out_id); 176 if (out_id > 0) 177 { 178 bool next_stop = false; 179 /* The last output found */ 180 do 181 { 182 /* Loop through all outputs */ 183 string key_string2 = key2.to!(string); 184 command_out_key cmd_key2; 185 parse_key_for_command_out(key_string2, cmd_key2); 186 //writefln("cmd_id=%s, out_id=%s", cmd_key2.cmd_id, cmd_key2.out_id); 187 if (cmd_key.id != cmd_key2.cmd_id || cwd != cmd_key2.cwd) 188 { 189 out_id = 0; 190 break; 191 } 192 else 193 { 194 if (next_stop) 195 { 196 nav_cmd_id = cmd_key2.cmd_id; 197 nav_out_id = cmd_key2.out_id; 198 //writefln("nav_cmd_id=%s, nav_out_id=%s", nav_cmd_id, nav_out_id); 199 next_stop=false; 200 201 int line_height9 = cast(int)(round(SQRT2^^fontsize)*1.2); 202 y = y_off - gs.screen.h + line_height9*3 + 8; 203 //writefln("OUT y = %d", y); 204 break; 205 } 206 207 //writefln("out_id=%s", cmd_key2.out_id); 208 string data_string2 = data2.to!(string); 209 command_out_data cmd_data2; 210 parse_data_for_command_out(data_string2, cmd_data2); 211 212 auto color = SDL_Color(0xFF,0xFF,0xFF,0xFF); 213 if (cmd_data2.pipe == OutPipe.STDERR) 214 { 215 color = SDL_Color(0xFF,0x80,0x80,0xFF); 216 } 217 218 line_height = cast(int)(round(SQRT2^^fontsize)*1.2); 219 220 final switch(cmd_data2.vers) 221 { 222 case CommandsOutVersion.Simple: 223 rt = gs.text_viewer.font.get_size_of_line(cmd_data2.output, 224 fontsize, gs.screen.w-80, line_height, color); 225 226 lines = rt.h / line_height - 1; 227 if (cmd_data2.output.length > 0 && cmd_data2.output[$-1] != '\n') lines++; 228 break; 229 230 case CommandsOutVersion.Screen: 231 rt = gs.text_viewer.font.get_size_of_line(cmd_data2.cols, 232 cmd_data2.rows, fontsize, color); 233 lines = cmd_data2.rows; 234 break; 235 } 236 237 238 rect = SDL_Rect(); 239 rect.x = 40; 240 rect.y = cast(int)(y_off + 4); 241 rect.w = rt.w; 242 rect.h = rt.h; 243 244 if (cmd_key2.cmd_id == mouse.cmd_id && 245 cmd_key2.out_id == mouse.out_id) 246 { 247 int ry = cast(int)(gs.mouse_screen_y - mouse_rel_y*rect.h); 248 y_off = ry - 4 + neg_y; 249 //writefln("on mouse out mouse.cmd_id=%s, mouse.out_id=%d", mouse.cmd_id, mouse.out_id); 250 //writefln("on mouse out ry-4=%d y_off=%s", ry-4, y_off); 251 } 252 253 //writefln("OUT cmd_key2.cmd_id=%s, out_id=%s %s, rect.y = %s > %s", cmd_key2.cmd_id, cmd_key2.out_id, cmd_data2.output, rect.y, gs.screen.h + line_height); 254 if (rect.y > gs.screen.h + line_height) 255 { 256 next_stop = true; 257 cmd_next_stop = true; 258 } 259 260 y_off += line_height*lines; 261 } 262 } 263 while (cursor2.get(&key2, &data2, DB_NEXT) == 0); 264 265 if (next_stop) 266 { 267 nav_cmd_id = cmd_key.id; 268 nav_out_id = 0; 269 270 int line_height9 = cast(int)(round(SQRT2^^fontsize)*1.2); 271 y = y_off - gs.screen.h + line_height9*3 + 8; 272 //writefln("OUT y = %d", y); 273 } 274 } 275 } 276 } 277 } 278 while (cursor.get(&key, &data, DB_NEXT) == 0); 279 280 if (cmd_next_stop) 281 { 282 nav_skip_cmd_id = 0; 283 if (fontsize < 5) 284 { 285 int line_height9 = cast(int)(round(SQRT2^^fontsize)*1.2); 286 y = y_off - gs.screen.h + line_height9*3 + 8; 287 //writefln("CMD y = %d", y); 288 } 289 } 290 } 291 neg_y = 0; 292 } 293 } 294 295 package void 296 selection_to_buffer(GlobalState gs) 297 { 298 with (gs.command_line) 299 { 300 if (start_selection.cmd_id == 0 || end_selection.cmd_id == 0) 301 return; 302 303 string selection = ""; 304 305 cwd = gs.current_path; 306 307 Dbc cursor = gs.db_commands.cursor(null, 0); 308 scope(exit) cursor.close(); 309 310 Dbt key, data; 311 312 ulong id = start_selection.cmd_id; 313 string ks = get_key_for_command(command_key(cwd, id)); 314 //writefln("SET RANGE: %s (cwd=%s, id=%d)", ks, cwd, id); 315 key = ks; 316 id = find_next_by_key(cursor, 0, id, key, data); 317 318 //writefln("mouse.cmd_id=%s", mouse.cmd_id); 319 if (id != 0) 320 { 321 bool cmd_next_stop; 322 /* The bottom command found */ 323 long y_off = neg_y; 324 //writefln("y=%s, y_off=%s", y, y_off); 325 do 326 { 327 string key_string = key.to!(string); 328 command_key cmd_key; 329 parse_key_for_command(key_string, cmd_key); 330 if (cwd != cmd_key.cwd) 331 { 332 id = 0; 333 break; 334 } 335 else 336 { 337 //writefln("cmd_id=%s", id); 338 id = cmd_key.id; 339 string data_string = data.to!(string); 340 command_data cmd_data; 341 parse_data_for_command(data_string, cmd_data); 342 343 ssize_t start_pos, end_pos; 344 get_start_end_pos(cmd_key.id, 345 0, 346 start_selection, 347 end_selection, 348 start_pos, end_pos); 349 if (start_pos >= 0) 350 { 351 //writefln("start_pos=%s end_pos=%s", start_pos, end_pos); 352 if (start_pos == 0) 353 selection ~= "$ "; 354 if (end_pos+1 >= cmd_data.command.length) end_pos = cmd_data.command.length-1; 355 selection ~= cmd_data.command[start_pos..end_pos+1]; 356 if (end_pos+1 == cmd_data.command.length) 357 selection ~= "\n"; 358 } 359 else if ( !(start_selection.cmd_id == cmd_key.id && start_selection.out_id > 0) ) 360 { 361 break; 362 } 363 364 /* Try to find last output for command */ 365 Dbc cursor2 = gs.db_command_output.cursor(null, 0); 366 scope(exit) cursor2.close(); 367 368 Dbt key2, data2; 369 ulong out_id = cmd_key.id == start_selection.cmd_id && 370 start_selection.out_id != 0 ? start_selection.out_id : 1; 371 ks = get_key_for_command_out(command_out_key(cwd, cmd_key.id, out_id)); 372 //writefln("SET RANGE: %s (cwd=%s, key_id=%d, out_id=%s)", ks, cwd, cmd_key.id, out_id); 373 key2 = ks; 374 out_id = find_next_by_key(cursor2, 0, out_id, key2, data2); 375 376 //writefln("mouse.out_id=%s", mouse.out_id); 377 if (out_id > 0) 378 { 379 do 380 { 381 /* Loop through all outputs */ 382 string key_string2 = key2.to!(string); 383 command_out_key cmd_key2; 384 parse_key_for_command_out(key_string2, cmd_key2); 385 //writefln("cmd_id=%s, out_id=%s", cmd_key2.cmd_id, cmd_key2.out_id); 386 if (cmd_key.id != cmd_key2.cmd_id || cwd != cmd_key2.cwd) 387 { 388 out_id = 0; 389 break; 390 } 391 else 392 { 393 //writefln("out_id=%s", cmd_key2.out_id); 394 string data_string2 = data2.to!(string); 395 command_out_data cmd_data2; 396 parse_data_for_command_out(data_string2, cmd_data2); 397 398 get_start_end_pos(cmd_key2.cmd_id, 399 cmd_key2.out_id, 400 start_selection, 401 end_selection, 402 start_pos, end_pos); 403 404 if (start_pos < 0) break; 405 if (end_pos+1 >= cmd_data2.output.length) end_pos = cmd_data2.output.length-1; 406 407 final switch(cmd_data2.vers) 408 { 409 case CommandsOutVersion.Simple: 410 selection ~= cmd_data2.output[start_pos..end_pos+1]; 411 break; 412 case CommandsOutVersion.Screen: 413 selection ~= to!string(cmd_data2.screen[start_pos..end_pos+1]); 414 break; 415 } 416 } 417 } 418 while (cursor2.get(&key2, &data2, DB_NEXT) == 0); 419 } 420 } 421 } 422 while (cursor.get(&key, &data, DB_NEXT) == 0); 423 } 424 //writefln("SELECTION:\n%s", selection); 425 SDL_SetClipboardText(selection.toStringz()); 426 } 427 } 428 429 void cmd_selection_to_buffer(GlobalState gs) 430 { 431 with (gs.command_line) 432 { 433 string selection = command[cmd_start_selection..cmd_end_selection + command.mystride(cmd_end_selection)]; 434 SDL_SetClipboardText(selection.toStringz()); 435 } 436 } 437 438 void shift_selected(GlobalState gs) 439 { 440 with (gs.command_line) 441 { 442 if (cmd_start_selection < 0 || cmd_end_selection < 0) return; 443 if (cmd_end_selection > command.length) 444 cmd_end_selection = command.length - 1 - command.strideBack(command.length - 1); 445 string converted; 446 for (ssize_t i=cmd_start_selection; i < cmd_end_selection + command.mystride(cmd_end_selection); i+=command.stride(i)) 447 { 448 string chr = command[i..i+command.stride(i)]; 449 if (chr.toLower() == chr) 450 { 451 chr = chr.toUpper(); 452 } 453 else 454 chr = chr.toLower(); 455 456 converted ~= chr; 457 } 458 459 command = command[0..cmd_start_selection] ~ converted ~ command[cmd_end_selection + command.mystride(cmd_end_selection)..$]; 460 } 461 } 462 463 bool find_chr(string chr, string[][3] *letters, ref ButtonPos buttonpos) 464 { 465 for (buttonpos.i = 0; buttonpos.i < 3; buttonpos.i++) 466 { 467 for (buttonpos.pos = 0; buttonpos.pos < 468 (*letters)[buttonpos.i].length; buttonpos.pos++) 469 { 470 if ((*letters)[buttonpos.i][buttonpos.pos] == chr) 471 return true; 472 } 473 } 474 return false; 475 } 476 477 void change_layout_selected(GlobalState gs) 478 { 479 with (gs.command_line) 480 { 481 if (cmd_start_selection < 0 || cmd_end_selection < 0) return; 482 if (cmd_end_selection > command.length) 483 cmd_end_selection = command.length - 1 - command.strideBack(command.length - 1); 484 ssize_t end_selection = cmd_end_selection + command.mystride(cmd_end_selection); 485 string converted; 486 for (ssize_t i=cmd_start_selection; i < end_selection; i+=command.stride(i)) 487 { 488 string chr = command[i..i+command.stride(i)]; 489 490 ssize_t prev_mode = gs.keybar.mode-1; 491 if (prev_mode < 0) 492 prev_mode = gs.keybar.layout_modes.length - 1; 493 494 ButtonPos buttonpos; 495 auto letters = &gs.keybar.layout_modes[prev_mode].letters; 496 if (find_chr(chr, letters, buttonpos)) 497 { 498 letters = &gs.keybar.layout_modes[gs.keybar.mode].letters; 499 chr = (*letters)[buttonpos.i][buttonpos.pos]; 500 } 501 letters = &gs.keybar.layout_modes[prev_mode].letters_shift; 502 if (find_chr(chr, letters, buttonpos)) 503 { 504 letters = &gs.keybar.layout_modes[gs.keybar.mode].letters_shift; 505 chr = (*letters)[buttonpos.i][buttonpos.pos]; 506 } 507 letters = &gs.keybar.layout_modes[prev_mode].letters_altgr; 508 if (find_chr(chr, letters, buttonpos)) 509 { 510 letters = &gs.keybar.layout_modes[gs.keybar.mode].letters_altgr; 511 chr = (*letters)[buttonpos.i][buttonpos.pos]; 512 } 513 letters = &gs.keybar.layout_modes[prev_mode].letters_shift_altgr; 514 if (find_chr(chr, letters, buttonpos)) 515 { 516 letters = &gs.keybar.layout_modes[gs.keybar.mode].letters_shift_altgr; 517 chr = (*letters)[buttonpos.i][buttonpos.pos]; 518 } 519 520 converted ~= chr; 521 } 522 523 command = command[0..cmd_start_selection] ~ converted ~ command[end_selection..$]; 524 cmd_end_selection = cmd_start_selection + converted.length; 525 cmd_end_selection -= command.strideBack(cmd_end_selection); 526 if (pos > cmd_start_selection) pos = command.length; 527 } 528 } 529 530 531 bool is_command_position(GlobalState gs, string command, ssize_t pos) 532 { 533 for (ssize_t i = pos-1; i >= -1; i--) 534 { 535 if (i == -1 || command[i] == '&' || command[i] == '|') 536 { 537 return true; 538 } 539 else if (command[i] != ' ' && command[i] != '\t') 540 { 541 return false; 542 } 543 } 544 return true; 545 } 546 547 enum StringStatus{ 548 Normal, 549 Quote, 550 DblQuote, 551 BackApostrophe 552 } 553 554 void uniq(ref string[] strings) 555 { 556 for (ssize_t i = 0; i < strings.length-1; i++) 557 { 558 if (strings[i] == strings[i+1]) 559 { 560 strings = strings[0..i] ~ strings[i+1..$]; 561 i--; 562 } 563 } 564 } 565 566 ssize_t find_in_sorted(string[] hashstock, string needle, ssize_t pos = 0) 567 { 568 if (hashstock.length == 0 ) 569 return pos; 570 571 if ( needle == hashstock[$/2] ) 572 { 573 return pos+hashstock.length/2; 574 } 575 else if ( needle < hashstock[$/2] ) 576 { 577 return find_in_sorted(hashstock[0..$/2], needle, pos); 578 } 579 else 580 { 581 return find_in_sorted(hashstock[$/2+1..$], needle, pos+hashstock.length/2+1); 582 } 583 } 584 585 string remove_backslashes(string str) 586 { 587 bool prevslash; 588 for (ssize_t i = 0; i < str.length; i += str.stride(i)) 589 { 590 string chr = str[i..i+str.stride(i)]; 591 if (chr == "\\" && !prevslash) 592 { 593 str = str[0..i] ~ str[i+chr.length..$]; 594 if (i >= str.length) break; 595 prevslash = true; 596 } 597 if (chr != "\\") 598 prevslash = false; 599 } 600 return str; 601 } 602 603 string backslashes(string str, StringStatus status) 604 { 605 string[] chars_needed_backslashes = []; 606 final switch (status) 607 { 608 case StringStatus.Quote: 609 break; 610 case StringStatus.DblQuote: 611 chars_needed_backslashes = [`"`, "`"]; 612 break; 613 case StringStatus.BackApostrophe: 614 chars_needed_backslashes = [`"`, "`", "`", "`"]; 615 break; 616 case StringStatus.Normal: 617 chars_needed_backslashes = [`"`, "`", "'", " "]; 618 break; 619 } 620 621 for (ssize_t i = 0; i < str.length; i += str.stride(i)) 622 { 623 string chr = str[i..i+str.stride(i)]; 624 foreach (cnb; chars_needed_backslashes) 625 { 626 if (chr == cnb) 627 { 628 str = str[0..i] ~ `\` ~ str[i..$]; 629 i++; 630 } 631 } 632 } 633 return str; 634 } 635 636 string common_part(string a, string b) 637 { 638 string res; 639 for (ssize_t i = 0; i < a.length && i < b.length; i+=a.stride(i)) 640 { 641 string chr1 = a[i..i+a.stride(i)]; 642 string chr2 = b[i..i+b.stride(i)]; 643 if (chr1 == chr2) 644 res ~= chr1; 645 else break; 646 } 647 return res; 648 } 649 650 string autocomplete(GlobalState gs, string command, bool is_command, StringStatus status) 651 { 652 string[] completions; 653 if (is_command && command.indexOf(SL) < 0) 654 { 655 string path = environment["PATH"]; 656 auto paths = splitter(path, pathSeparator); 657 658 string[] binaries = []; 659 foreach(p; paths) 660 { 661 p = p.expandTilde(); 662 try 663 { 664 foreach (string name; dirEntries(p, SpanMode.shallow)) 665 { 666 binaries ~= name[name.lastIndexOf(SL)+1..$]; 667 } 668 } 669 catch (FileException exp) 670 { 671 } 672 } 673 674 binaries ~= ["cd", "select", "go", "open", "mopen"]; 675 sort!("a < b")(binaries); 676 uniq(binaries); 677 completions = binaries; 678 } 679 else 680 { 681 version (Windows) 682 { 683 if (gs.full_current_path == "") 684 { 685 string[] files = []; 686 files ~= "C:\\"; 687 files ~= "D:\\"; 688 files ~= "E:\\"; 689 completions = files; 690 } 691 692 } 693 if (gs.full_current_path > "") 694 { 695 string full_current_path = gs.full_current_path; 696 version (Windows) 697 { 698 if (full_current_path[1] == ':' && full_current_path.length == 2) 699 full_current_path ~= SL; 700 } 701 702 string dir; 703 dir = buildNormalizedPath(absolutePath(expandTilde(command), full_current_path)); 704 if (command.length > 0 && command[$-1] == SL[0]) dir ~= SL; 705 dir = dir[0..dir.lastIndexOf(SL)+1]; 706 707 string[] files = []; 708 if (gs.selection_hash.length > 0) 709 files ~= "${SELECTED[@]}"; 710 //writefln("dir = %s, is null = %s, %s", dir, dir is null, dir.length); 711 if (dir !is null) 712 { 713 try{ 714 if (!dir.isDir()) dir = dir[0..dir.lastIndexOf(SL)]; 715 foreach (string name; dirEntries(dir, SpanMode.shallow)) 716 { 717 files ~= name[name.lastIndexOf(SL)+1..$]; 718 } 719 720 command = command[command.lastIndexOf(SL)+1..$]; 721 722 sort!("a < b")(files); 723 completions = files; 724 } catch (FileException exp) 725 { 726 } 727 } 728 } 729 } 730 731 final switch (status) 732 { 733 case StringStatus.Quote: 734 break; 735 case StringStatus.DblQuote: 736 command = remove_backslashes(command); 737 break; 738 case StringStatus.BackApostrophe: 739 command = remove_backslashes(command); 740 break; 741 case StringStatus.Normal: 742 command = remove_backslashes(command); 743 break; 744 } 745 746 string[] next_chars = []; 747 ssize_t pos = find_in_sorted(completions, command); 748 ssize_t i; 749 for (i=pos; i < completions.length; i++) 750 { 751 if ( completions[i].startsWith(command) ) 752 { 753 if ( completions[i].length <= command.length ) 754 { 755 next_chars ~= "✔"; 756 continue; 757 } 758 if (next_chars.length == 0 || next_chars[$-1] != completions[i][command.length..command.length+completions[i].stride(command.length)]) 759 next_chars ~= completions[i][command.length..command.length+completions[i].stride(command.length)]; 760 } 761 else 762 break; 763 } 764 765 if (i == pos) 766 return ""; 767 else if (i == pos+1) 768 return "1"~completions[pos][command.length..$].backslashes(status); 769 else if (next_chars.length == 1) 770 { 771 string common = completions[pos]; 772 for (ssize_t j = pos+1; j < i; j++) 773 common = common_part(common, completions[j]); 774 return "1"~common[command.length..$].backslashes(status); 775 } 776 else if (i - pos <= 5) 777 { 778 string result = ""; 779 for (ssize_t j = pos; j < i; j++) 780 { 781 result ~= completions[j] ~ " "; 782 } 783 return "2"~result; 784 } 785 else 786 { 787 string result = ""; 788 foreach (chr; next_chars) 789 { 790 result ~= chr ~ " "; 791 } 792 return "2"~result; 793 } 794 } 795 796 string autocomplete(GlobalState gs, string command) 797 { 798 static string prev_command; 799 static string prev_result; 800 if (prev_command == command) 801 return prev_result; 802 803 prev_command = command; 804 805 if (command.length > 0 && (command[0] == '+' || command[0] == '*')) 806 command = command[1..$]; 807 808 ssize_t dbl_quotes = command.count("\""); 809 ssize_t esc_dbl_quotes = command.count("\\\""); 810 dbl_quotes -= esc_dbl_quotes; 811 812 ssize_t back_apostrophes = command.count("`"); 813 ssize_t esc_back_apostrophes = command.count("\\`"); 814 back_apostrophes -= esc_back_apostrophes; 815 816 ssize_t quotes = command.count("'"); 817 818 ssize_t quote; 819 if (quotes%2 == 1) 820 { 821 quote = command.lastIndexOf("'"); 822 } 823 824 ssize_t dbl_quote; 825 if (dbl_quotes%2 == 1) 826 { 827 dbl_quote = command.lastIndexOf("\""); 828 while (dbl_quote > 0 && command[dbl_quote-1] == '\\') 829 { 830 dbl_quote = command[0..dbl_quote].lastIndexOf("`"); 831 } 832 } 833 834 ssize_t back_apostrophe; 835 if (back_apostrophes%2 == 1) 836 { 837 back_apostrophe = command.lastIndexOf("`"); 838 while (back_apostrophe > 0 && command[back_apostrophe-1] == '\\') 839 { 840 back_apostrophe = command[0..back_apostrophe].lastIndexOf("`"); 841 } 842 } 843 844 bool is_command; 845 StringStatus string_status; 846 ssize_t pos; 847 if (quote > dbl_quote && quote > back_apostrophe) 848 { 849 is_command = is_command_position(gs, command, quote); 850 pos = quote+1; 851 string_status = StringStatus.Quote; 852 } 853 else if (dbl_quote > quote && dbl_quote > back_apostrophe) 854 { 855 is_command = is_command_position(gs, command, dbl_quote); 856 pos = dbl_quote+1; 857 string_status = StringStatus.DblQuote; 858 } 859 else 860 { 861 if (back_apostrophe > quote && back_apostrophe > dbl_quote) 862 { 863 command = command[dbl_quote+1..$]; 864 } 865 866 ssize_t space = command.lastIndexOf(" "); 867 while ( space > 0 && command[space-1] == '\\') 868 { 869 space = command[0..space].lastIndexOf(" "); 870 } 871 is_command = is_command_position(gs, command, space); 872 873 pos = space+1; 874 if (back_apostrophe > quote && back_apostrophe > dbl_quote) 875 { 876 string_status = StringStatus.BackApostrophe; 877 } 878 else 879 { 880 string_status = StringStatus.Normal; 881 } 882 } 883 884 prev_result = autocomplete(gs, command[pos..$], is_command, string_status); 885 return prev_result; 886 } 887 888 private void 889 get_start_end_pos(in ulong cmd_id, 890 in ulong out_id, 891 in CmdOutPos start_selection, 892 in CmdOutPos end_selection, 893 out ssize_t start_pos, out ssize_t end_pos) 894 { 895 if (cmd_id < start_selection.cmd_id || 896 cmd_id == start_selection.cmd_id && 897 out_id < start_selection.out_id || 898 cmd_id > end_selection.cmd_id || 899 cmd_id == end_selection.cmd_id && 900 out_id > end_selection.out_id) 901 { 902 start_pos = -1; 903 end_pos = -1; 904 return; 905 } 906 907 if (cmd_id > start_selection.cmd_id || 908 cmd_id == start_selection.cmd_id && 909 out_id > start_selection.out_id) 910 { 911 start_pos = 0; 912 } 913 914 if ( cmd_id == start_selection.cmd_id && 915 out_id == start_selection.out_id) 916 { 917 start_pos = start_selection.pos; 918 } 919 920 if (cmd_id < end_selection.cmd_id || 921 cmd_id == end_selection.cmd_id && 922 out_id< end_selection.cmd_id) 923 { 924 end_pos = ssize_t.max; 925 } 926 927 if ( cmd_id == end_selection.cmd_id && 928 out_id == end_selection.out_id) 929 { 930 end_pos = end_selection.pos; 931 if (end_pos == -1) 932 end_pos = ssize_t.max; 933 } 934 } 935 936 void 937 draw_command_line(GlobalState gs) 938 { 939 with (gs.command_line) 940 { 941 if (SDL_GetTicks() - last_redraw > 200) 942 { 943 on_click = null; 944 945 /* Setup texture render target */ 946 auto old_texture = SDL_GetRenderTarget(gs.renderer); 947 int r = SDL_SetRenderTarget(gs.renderer, texture); 948 if (r < 0) 949 { 950 throw new Exception(format("Error while set render target text_viewer.texture: %s", 951 fromStringz(SDL_GetError()) )); 952 } 953 954 SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND); 955 r = SDL_SetRenderDrawColor(gs.renderer, 0, 0, 0, 0); 956 if (r < 0) 957 { 958 writefln("Can't SDL_SetRenderDrawColor: %s", 959 to!string(SDL_GetError())); 960 } 961 redraw: 962 r = SDL_RenderClear(gs.renderer); 963 if (r < 0) 964 { 965 throw new Exception(format("Error while clear renderer: %s", 966 fromStringz(SDL_GetError()) )); 967 } 968 969 /* If terminal */ 970 if (gs.command_line.terminal) 971 { 972 if (font_changed || neg_y < 0) 973 { 974 fix_bottom_line(gs); 975 font_changed = false; 976 } 977 978 /* Background */ 979 r = SDL_RenderCopy(gs.renderer, gs.texture_black, null, null); 980 if (r < 0) 981 { 982 writefln( "draw_command_line(), 1: Error while render copy: %s", 983 SDL_GetError().to!string() ); 984 } 985 986 with (gs.command_line) 987 { 988 cwd = gs.current_path; 989 Dbc cursor = gs.db_commands.cursor(null, 0); 990 scope(exit) cursor.close(); 991 992 /* Try to find bottom command in commands history */ 993 bool first_cmd_or_out = true; 994 995 //writefln("nav_skip_cmd_id=%s", nav_skip_cmd_id); 996 Dbt key, data; 997 //writefln("Find prev nav_skip_cmd_id=%d", nav_skip_cmd_id); 998 ulong id = find_prev_command(cursor, cwd, nav_skip_cmd_id, 999 key, data); 1000 1001 if (id != 0) 1002 { 1003 /* The bottom command found */ 1004 int line_height9 = cast(int)(round(SQRT2^^fontsize)*1.2); 1005 long y_off = y + gs.screen.h - line_height9*3 - 8; 1006 //writefln("y=%s, y_off=%s", y, y_off); 1007 do 1008 { 1009 /* Loop through all commands */ 1010 string key_string = key.to!(string); 1011 command_key cmd_key; 1012 parse_key_for_command(key_string, cmd_key); 1013 if (cwd != cmd_key.cwd) 1014 { 1015 id = 0; 1016 break; 1017 } 1018 else 1019 { 1020 //writefln("cmd_id=%s found", cmd_key.id); 1021 if (fontsize >= 5 && !search_mode) 1022 { 1023 /* Try to find last output for command */ 1024 Dbc cursor2 = gs.db_command_output.cursor(null, 0); 1025 scope(exit) cursor2.close(); 1026 1027 Dbt key2, data2; 1028 ulong out_id = cmd_key.id == nav_cmd_id ? nav_out_id : 0; 1029 out_id = find_prev_command_out(cursor2, cwd, cmd_key.id, out_id, key2, data2); 1030 bool first_out = true; 1031 1032 if (out_id > 0) 1033 { 1034 /* The last output found */ 1035 do 1036 { 1037 /* Loop through all outputs */ 1038 string key_string2 = key2.to!(string); 1039 command_out_key cmd_key2; 1040 parse_key_for_command_out(key_string2, cmd_key2); 1041 //writefln("cmd_id=%s, out_id=%s", cmd_key2.cmd_id, cmd_key2.out_id); 1042 if (cmd_key.id != cmd_key2.cmd_id) 1043 { 1044 out_id = 0; 1045 break; 1046 } 1047 else 1048 { 1049 string data_string2 = data2.to!(string); 1050 command_out_data cmd_data2; 1051 parse_data_for_command_out(data_string2, cmd_data2); 1052 1053 auto color = SDL_Color(0xFF,0xFF,0xFF,0xFF); 1054 if (cmd_data2.pipe == OutPipe.STDERR) 1055 { 1056 color = SDL_Color(0xFF,0x80,0x80,0xFF); 1057 } 1058 int line_height = cast(int)(round(SQRT2^^fontsize)*1.2); 1059 1060 auto rect = SDL_Rect(); 1061 SDL_Rect rt; 1062 Texture_Tick *tt; 1063 final switch(cmd_data2.vers) 1064 { 1065 case CommandsOutVersion.Simple: 1066 //writefln("out_id=%s cmd_data2.output=%s", cmd_key2.out_id, cmd_data2.output); 1067 rt = gs.text_viewer.font.get_size_of_line(cmd_data2.output, 1068 fontsize, gs.screen.w-80, line_height, color); 1069 1070 int lines = rt.h / line_height - 1; 1071 if (cmd_data2.output.length > 0 && cmd_data2.output[$-1] != '\n') lines++; 1072 y_off -= line_height*lines; 1073 1074 auto i = 0; 1075 rect.x = 40; 1076 rect.y = cast(int)(y_off + 4 + line_height*i); 1077 rect.w = rt.w; 1078 rect.h = rt.h; 1079 1080 if (rect.y < gs.screen.h && rect.y+rect.h > 0) 1081 { 1082 /* Selection */ 1083 ssize_t start_pos, end_pos; 1084 get_start_end_pos(cmd_key2.cmd_id, 1085 cmd_key2.out_id, 1086 start_selection, 1087 end_selection, 1088 start_pos, end_pos); 1089 1090 tt = gs.text_viewer.font.get_line_from_cache(cmd_data2.output, 1091 fontsize, gs.screen.w-80, line_height, color, 1092 cmd_data2.attrs, start_pos, end_pos); 1093 if (!tt && !tt.texture) 1094 { 1095 throw new Exception("Can't create text_surface: "~ 1096 to!string(TTF_GetError())); 1097 } 1098 //assert(rt.w == tt.w && rt.h == tt.h, format("rt.w=%d, tt.w=%d, rt.h=%d, tt.h=%d", 1099 // rt.w, tt.w, rt.h, tt.h)); 1100 1101 r = SDL_RenderCopy(gs.renderer, tt.texture, null, &rect); 1102 if (r < 0) 1103 { 1104 writefln( 1105 "draw_command_line(), 2: Error while render copy: %s", 1106 SDL_GetError().to!string() ); 1107 writefln("text: %s", cmd_data2.output); 1108 } 1109 1110 /* Draw cursor */ 1111 if (first_out && cmd_data2.pos < tt.chars.length && 1112 command_in_focus_id == cmd_key2.cmd_id) 1113 { 1114 auto rect2= tt.chars[cmd_data2.pos]; 1115 rect2.x += 40; 1116 rect2.y += cast(int)(y_off + 4 + line_height*i); 1117 string chr = " "; 1118 try 1119 { 1120 if (cmd_data2.pos >= 0 && cmd_data2.pos < cmd_data2.output.length && cmd_data2.pos + cmd_data2.output.mystride(cmd_data2.pos) < cmd_data2.output.length) 1121 chr = cmd_data2.output[cmd_data2.pos..cmd_data2.pos+cmd_data2.output.mystride(cmd_data2.pos)]; 1122 } 1123 catch (UTFException exp) 1124 { 1125 chr = " "; 1126 } 1127 if (chr == "\n") chr = " "; 1128 1129 r = SDL_RenderCopy(gs.renderer, gs.texture_cursor, null, &rect2); 1130 if (r < 0) 1131 { 1132 writefln( "draw_command_line(), 3: Error while render copy: %s", 1133 SDL_GetError().to!string() ); 1134 } 1135 1136 auto st = gs.text_viewer.font.get_char_from_cache(chr, fontsize, SDL_Color(0x00, 0x00, 0x20, 0xFF)); 1137 if (!st) return; 1138 1139 r = SDL_RenderCopy(gs.renderer, st.texture, null, &rect2); 1140 if (r < 0) 1141 { 1142 writefln( 1143 "draw_command_line(), 4: Error while render copy: %s", 1144 SDL_GetError().to!string() ); 1145 writefln("chr: %s", chr); 1146 } 1147 } 1148 } 1149 break; 1150 case CommandsOutVersion.Screen: 1151 rt = gs.text_viewer.font.get_size_of_line(cmd_data2.cols, 1152 cmd_data2.rows, fontsize, color); 1153 1154 int lines = rt.h / line_height - 1; 1155 if (cmd_data2.output.length > 0 && cmd_data2.output[$-1] != '\n') lines++; 1156 y_off -= line_height*lines; 1157 1158 auto i = 0; 1159 rect.x = 40; 1160 rect.y = cast(int)(y_off + 4 + line_height*i); 1161 rect.w = rt.w; 1162 rect.h = rt.h; 1163 1164 if (rect.y < gs.screen.h && rect.y+rect.h > 0) 1165 { 1166 /* Selection */ 1167 ssize_t start_pos, end_pos; 1168 get_start_end_pos(cmd_key2.cmd_id, 1169 cmd_key2.out_id, 1170 start_selection, 1171 end_selection, 1172 start_pos, end_pos); 1173 1174 tt = gs.text_viewer.font.get_line_from_cache(cmd_data2.screen, 1175 cmd_data2.cols, cmd_data2.rows, 1176 fontsize, line_height, color, 1177 cmd_data2.scr_attrs, start_pos, end_pos); 1178 if (!tt && !tt.texture) 1179 { 1180 throw new Exception("Can't create text_surface: "~ 1181 to!string(TTF_GetError())); 1182 } 1183 //assert(rt.w == tt.w && rt.h == tt.h, format("rt.w=%d, tt.w=%d, rt.h=%d, tt.h=%d", 1184 // rt.w, tt.w, rt.h, tt.h)); 1185 1186 r = SDL_RenderCopy(gs.renderer, tt.texture, null, &rect); 1187 if (r < 0) 1188 { 1189 writefln( 1190 "draw_command_line(), 2: Error while render copy: %s", 1191 SDL_GetError().to!string() ); 1192 writefln("text: %s", cmd_data2.output); 1193 } 1194 1195 /* Draw cursor */ 1196 if (first_out && cmd_data2.pos < tt.chars.length && 1197 command_in_focus_id == cmd_key2.cmd_id) 1198 { 1199 auto rect2= tt.chars[cmd_data2.pos]; 1200 rect2.x += 40; 1201 rect2.y += cast(int)(y_off + 4 + line_height*i); 1202 string chr = " "; 1203 try 1204 { 1205 chr = to!string(cmd_data2.screen[cmd_data2.pos]); 1206 } 1207 catch (UTFException exp) 1208 { 1209 chr = " "; 1210 } 1211 if (chr == "\n") chr = " "; 1212 1213 r = SDL_RenderCopy(gs.renderer, gs.texture_cursor, null, &rect2); 1214 if (r < 0) 1215 { 1216 writefln( "draw_command_line(), 3: Error while render copy: %s", 1217 SDL_GetError().to!string() ); 1218 } 1219 1220 auto st = gs.text_viewer.font.get_char_from_cache(chr, fontsize, SDL_Color(0x00, 0x00, 0x20, 0xFF)); 1221 if (!st) return; 1222 1223 r = SDL_RenderCopy(gs.renderer, st.texture, null, &rect2); 1224 if (r < 0) 1225 { 1226 writefln( 1227 "draw_command_line(), 4: Error while render copy: %s", 1228 SDL_GetError().to!string() ); 1229 writefln("chr: %s", chr); 1230 } 1231 } 1232 } 1233 1234 break; 1235 } 1236 1237 first_out = false; 1238 1239 if (rect.y <= gs.mouse_screen_y && 1240 rect.y+rect.h-line_height >= gs.mouse_screen_y && neg_y >= 0) 1241 { 1242 mouse.cmd_id = cmd_key2.cmd_id; 1243 mouse.out_id = cmd_key2.out_id; 1244 mouse.pos = get_position_by_chars( 1245 gs.mouse_screen_x - rect.x, 1246 gs.mouse_screen_y - rect.y, tt.chars); 1247 mouse_rel_y = (cast(double)gs.mouse_screen_y-rect.y)/rect.h; 1248 //writefln("OUT: mouse.cmd_id=%s, mouse.out_id=%s, gs.mouse_screen_y=%s rect.y=%s rect.h=%s, mouse_rel_y=%s", 1249 // mouse.cmd_id, mouse.out_id, gs.mouse_screen_y, rect.y, rect.h, mouse_rel_y); 1250 } 1251 1252 if (rect.y > gs.screen.h) 1253 { 1254 //writefln("tt.h=%s", tt.h); 1255 //writefln("rect.y-gs.screen.h=%s", rect.y-gs.screen.h); 1256 y -= rt.h - line_height; 1257 nav_out_id = cmd_key2.out_id; 1258 nav_cmd_id = cmd_key.id; 1259 //writefln("OUT UP"); 1260 } 1261 1262 //writefln("OUT: y=%s, first_cmd_or_out=%s, nav_out_id=%s, rect.y + rect.h=%s, gs.screen.h=%s", 1263 // y, first_cmd_or_out, nav_out_id, rect.y + rect.h, gs.screen.h); 1264 if (y != 0 && first_cmd_or_out && nav_out_id > 0 && (rect.y + rect.h) < gs.screen.h) 1265 { 1266 fix_bottom_line(gs); 1267 goto redraw; 1268 } 1269 first_cmd_or_out = false; 1270 1271 if (rect.y + rect.h < 0) 1272 break; 1273 } 1274 } 1275 while (cursor2.get(&key2, &data2, DB_PREV) == 0); 1276 1277 } 1278 } 1279 1280 //writefln("Excellent"); 1281 id = cmd_key.id; 1282 string data_string = data.to!(string); 1283 command_data cmd_data; 1284 parse_data_for_command(data_string, cmd_data); 1285 1286 if (search_mode && cmd_data.command.indexOf(search) < 0) 1287 continue; 1288 1289 int line_height = cast(int)(round(SQRT2^^9)*1.2); 1290 auto rt = gs.text_viewer.font.get_size_of_line(cmd_data.command, 1291 9, gs.screen.w-120, line_height, SDL_Color(0xFF,0x00,0xFF,0xFF)); 1292 1293 int lines = rt.h / line_height; 1294 y_off -= line_height*lines; 1295 1296 /* Draw Status of cmd*/ 1297 if (cmd_data.end == 0) 1298 { 1299 string symbol; 1300 SDL_Color color; 1301 if (cmd_key.id in gs.tid_by_command_id) 1302 { 1303 symbol = "⬤"; 1304 color = SDL_Color(0x00, 0xFF, 0x00, 0xFF); 1305 } 1306 else 1307 { 1308 symbol = "◯"; 1309 color = SDL_Color(0xFF, 0x00, 0x00, 0xFF); 1310 } 1311 auto tt = gs.text_viewer.font.get_char_from_cache( 1312 symbol, 7, color); 1313 1314 auto rect = SDL_Rect(); 1315 rect.x = 30; 1316 rect.y = cast(int)(y_off + 4); 1317 rect.w = tt.w; 1318 rect.h = tt.h; 1319 1320 r = SDL_RenderCopy(gs.renderer, tt.texture, null, &rect); 1321 if (r < 0) 1322 { 1323 writefln( 1324 "draw_command_line(), 5: Error while render copy: %s", 1325 SDL_GetError().to!string() ); 1326 } 1327 } 1328 else 1329 { 1330 auto color = SDL_Color(0x00,0xFF,0x00,0xFF); 1331 if (cmd_data.status != 0) color = SDL_Color(0xFF,0x00,0x00,0xFF); 1332 1333 auto tt = gs.text_viewer.font.get_line_from_cache(format("%d", cmd_data.status), 1334 8, gs.screen.w-120, line_height, color); 1335 if (!tt && !tt.texture) 1336 { 1337 throw new Exception("Can't create text_surface: "~ 1338 to!string(TTF_GetError())); 1339 } 1340 1341 auto rect = SDL_Rect(); 1342 rect.x = 30; 1343 rect.y = cast(int)(y_off + 4); 1344 rect.w = tt.w; 1345 rect.h = tt.h; 1346 1347 r = SDL_RenderCopy(gs.renderer, tt.texture, null, &rect); 1348 if (r < 0) 1349 { 1350 writefln( 1351 "draw_command_line(), 6: Error while render copy: %s", 1352 SDL_GetError().to!string() ); 1353 } 1354 } 1355 1356 auto i = 0; 1357 auto rect = SDL_Rect(); 1358 rect.x = 60; 1359 rect.y = cast(int)(y_off + 4 + line_height*i); 1360 rect.w = rt.w; 1361 rect.h = rt.h; 1362 1363 Texture_Tick *tt; 1364 if (rect.y < gs.screen.h && rect.y+rect.h > 0) 1365 { 1366 /* Draw command */ 1367 /* Selection */ 1368 ssize_t start_pos, end_pos; 1369 get_start_end_pos(cmd_key.id, 1370 0, 1371 start_selection, 1372 end_selection, 1373 start_pos, end_pos); 1374 tt = gs.text_viewer.font.get_line_from_cache(cmd_data.command, 1375 9, gs.screen.w-80, line_height, SDL_Color(0xFF,0x00,0xFF,0xFF), 1376 null, start_pos, end_pos); 1377 if (!tt && !tt.texture) 1378 { 1379 throw new Exception("Can't create text_surface: "~ 1380 to!string(TTF_GetError())); 1381 } 1382 1383 r = SDL_RenderCopy(gs.renderer, tt.texture, null, &rect); 1384 if (r < 0) 1385 { 1386 writefln( 1387 "draw_command_line(), 7: Error while render copy: %s", 1388 SDL_GetError().to!string() ); 1389 } 1390 1391 if (cmd_data.end == 0 && cmd_key.id in gs.tid_by_command_id) 1392 { 1393 /* Control elements for running processes */ 1394 auto color = SDL_Color(0x80,0x80,0x00,0xFF); 1395 1396 /*SIGTERM*/ 1397 auto tt2 = gs.text_viewer.font.get_line_from_cache("TERM", 1398 8, gs.screen.w-80, line_height, color); 1399 if (!tt2 && !tt2.texture) 1400 { 1401 throw new Exception("Can't create text_surface: "~ 1402 to!string(TTF_GetError())); 1403 } 1404 1405 auto rect2 = SDL_Rect(); 1406 rect2.x = rect.x+rect.w + 5; 1407 rect2.y = rect.y+3; 1408 rect2.w = tt2.w; 1409 rect2.h = tt2.h; 1410 1411 if (gs.mouse_screen_x >= rect2.x && gs.mouse_screen_x <= rect2.x+rect2.w && 1412 gs.mouse_screen_y >= rect2.y && gs.mouse_screen_y <= rect2.y+rect2.h) 1413 { 1414 color = SDL_Color(0xFF,0xFF,0x00,0xFF); 1415 tt2 = gs.text_viewer.font.get_line_from_cache("TERM", 1416 8, gs.screen.w-80, line_height, color); 1417 if (!tt2 && !tt2.texture) 1418 { 1419 throw new Exception("Can't create text_surface: "~ 1420 to!string(TTF_GetError())); 1421 } 1422 1423 auto get_handler1(Tid tid) 1424 { 1425 return () 1426 { 1427 send(tid, "signal", SIGTERM); 1428 }; 1429 } 1430 1431 on_click = get_handler1(gs.tid_by_command_id[cmd_key.id]); 1432 } 1433 1434 r = SDL_RenderCopy(gs.renderer, tt2.texture, null, &rect2); 1435 if (r < 0) 1436 { 1437 writefln( 1438 "draw_command_line(), 8: Error while render copy: %s", 1439 SDL_GetError().to!string() ); 1440 } 1441 1442 /*SIGKILL*/ 1443 color = SDL_Color(0x80,0x00,0x00,0x80); 1444 auto tt3 = gs.text_viewer.font.get_line_from_cache("KILL", 1445 8, gs.screen.w-80, line_height, color); 1446 if (!tt3 && !tt3.texture) 1447 { 1448 throw new Exception("Can't create text_surface: "~ 1449 to!string(TTF_GetError())); 1450 } 1451 1452 auto rect3 = SDL_Rect(); 1453 rect3.x = rect2.x+rect2.w + 5; 1454 rect3.y = rect2.y; 1455 rect3.w = tt3.w; 1456 rect3.h = tt3.h; 1457 1458 if (gs.mouse_screen_x >= rect3.x && gs.mouse_screen_x <= rect3.x+rect3.w && 1459 gs.mouse_screen_y >= rect3.y && gs.mouse_screen_y <= rect3.y+rect3.h) 1460 { 1461 color = SDL_Color(0xFF,0x00,0x00,0x80); 1462 tt3 = gs.text_viewer.font.get_line_from_cache("KILL", 1463 8, gs.screen.w-80, line_height, color); 1464 if (!tt2 && !tt2.texture) 1465 { 1466 throw new Exception("Can't create text_surface: "~ 1467 to!string(TTF_GetError())); 1468 } 1469 1470 auto get_handler2(Tid tid) 1471 { 1472 return () 1473 { 1474 version(Posix) 1475 { 1476 send(tid, "signal", SIGKILL); 1477 } 1478 else version (Windows) 1479 { 1480 writefln("send SIGKILL Signal in windows not supported (Tid=%s)", tid); 1481 } 1482 }; 1483 } 1484 1485 on_click = get_handler2(gs.tid_by_command_id[cmd_key.id]); 1486 } 1487 1488 r = SDL_RenderCopy(gs.renderer, tt3.texture, null, &rect3); 1489 if (r < 0) 1490 { 1491 writefln( 1492 "draw_command_line(), 9: Error while render copy: %s", 1493 SDL_GetError().to!string() ); 1494 } 1495 } 1496 else 1497 { 1498 /* Control elements for not running processes */ 1499 1500 /*PLAY*/ 1501 auto color = SDL_Color(0x00,0x80,0x00,0xFF); 1502 auto tt2 = gs.text_viewer.font.get_char_from_cache( 1503 "⏵", 9, color); 1504 1505 auto rect2 = SDL_Rect(); 1506 rect2.x = rect.x+rect.w; 1507 rect2.y = rect.y; 1508 rect2.w = tt2.w; 1509 rect2.h = tt2.h; 1510 1511 if (gs.mouse_screen_x >= rect2.x && gs.mouse_screen_x <= rect2.x+rect2.w && 1512 gs.mouse_screen_y >= rect2.y && gs.mouse_screen_y <= rect2.y+rect2.h) 1513 { 1514 color = SDL_Color(0x00,0xFF,0x00,0xFF); 1515 tt2 = gs.text_viewer.font.get_char_from_cache( 1516 "⏵", 9, color); 1517 auto get_handler3(string command) 1518 { 1519 return () 1520 { 1521 run_command(gs, command); 1522 }; 1523 } 1524 on_click = get_handler3(cmd_data.command.idup()); 1525 } 1526 1527 r = SDL_RenderCopy(gs.renderer, tt2.texture, null, &rect2); 1528 if (r < 0) 1529 { 1530 writefln( 1531 "draw_command_line(), 10: Error while render copy: %s", 1532 SDL_GetError().to!string() ); 1533 } 1534 1535 /*REMOVE*/ 1536 color = SDL_Color(0x80,0x00,0x00,0xFF); 1537 auto tt3 = gs.text_viewer.font.get_char_from_cache( 1538 "❌", 9, color); 1539 1540 auto rect3 = SDL_Rect(); 1541 rect3.x = rect2.x+rect2.w + 5; 1542 rect3.y = rect2.y; 1543 rect3.w = tt3.w; 1544 rect3.h = tt3.h; 1545 1546 if (gs.mouse_screen_x >= rect3.x && gs.mouse_screen_x <= rect3.x+rect3.w && 1547 gs.mouse_screen_y >= rect3.y && gs.mouse_screen_y <= rect3.y+rect3.h) 1548 { 1549 color = SDL_Color(0xFF,0x00,0x00,0x80); 1550 tt3 = gs.text_viewer.font.get_char_from_cache( 1551 "❌", 9, color); 1552 1553 auto get_handler4(string cwd, ulong cmd_id) 1554 { 1555 return () 1556 { 1557 delete_command(gs, cwd, cmd_id); 1558 }; 1559 } 1560 1561 on_click = get_handler4(cmd_key.cwd.idup(), cmd_key.id); 1562 } 1563 1564 r = SDL_RenderCopy(gs.renderer, tt3.texture, null, &rect3); 1565 if (r < 0) 1566 { 1567 writefln( 1568 "draw_command_line(), 11: Error while render copy: %s", 1569 SDL_GetError().to!string() ); 1570 } 1571 } 1572 1573 } 1574 1575 if (rect.y <= gs.mouse_screen_y && 1576 rect.y+rect.h >= gs.mouse_screen_y && neg_y >= 0) 1577 { 1578 mouse.cmd_id = cmd_key.id; 1579 mouse.out_id = 0; 1580 mouse.pos = get_position_by_chars( 1581 gs.mouse_screen_x - rect.x, 1582 gs.mouse_screen_y - rect.y, tt.chars); 1583 mouse_rel_y = (cast(double)gs.mouse_screen_y-rect.y)/rect.h; 1584 //writefln("CMD: mouse.cmd_id=%s, mouse.out_id=%s, command=%s", 1585 // mouse.cmd_id, mouse.out_id, cmd_data.command); 1586 } 1587 1588 if (rect.y > gs.screen.h) 1589 { 1590 y -= rt.h; 1591 nav_skip_cmd_id = cmd_key.id; 1592 //writefln("CMD UP"); 1593 } 1594 1595 //writefln("CMD: y=%s, first_cmd_or_out=%s, nav_skip_cmd_id=%s, rect.y + rect.h=%s, gs.screen.h=%s", 1596 // y, first_cmd_or_out, nav_skip_cmd_id, rect.y + rect.h, gs.screen.h); 1597 /*if (y != 0 && first_cmd_or_out && nav_skip_cmd_id > 0 && (rect.y + rect.h) < gs.screen.h) 1598 { 1599 //FYI: This loop maybe be infinite 1600 fix_bottom_line(gs); 1601 goto redraw; 1602 }*/ 1603 1604 first_cmd_or_out = false; 1605 1606 if (rect.y + rect.h < 0) 1607 break; 1608 } 1609 } 1610 while (cursor.get(&key, &data, DB_PREV) == 0); 1611 } 1612 1613 if (first_cmd_or_out) 1614 { 1615 fix_bottom_line(gs); 1616 //goto redraw; 1617 } 1618 } 1619 } 1620 1621 if (gs.command_line.enter) 1622 { 1623 long y_off; 1624 1625 string prompt = "$ "; 1626 if (search_mode && hist_pos == 0) 1627 prompt ="<search> "; 1628 int line_height = cast(int)(round(SQRT2^^9)*1.2); 1629 auto ptt = gs.text_viewer.font.get_line_from_cache(prompt, 1630 9, gs.screen.w-80, line_height, SDL_Color(0x00,0xFF,0x00,0xFF)); 1631 if (!ptt && !ptt.texture) 1632 { 1633 throw new Exception("Can't create text_surface: "~ 1634 to!string(TTF_GetError())); 1635 } 1636 1637 string str = command; 1638 if (search_mode && hist_pos == 0) 1639 str = search; 1640 1641 auto tt = gs.text_viewer.font.get_line_from_cache(str, 1642 9, gs.screen.w-80-ptt.w, line_height, SDL_Color(0xFF, 0xFF, 0xFF, 0xFF), 1643 null, cmd_start_selection, cmd_end_selection); 1644 if (!tt && !tt.texture) 1645 { 1646 throw new Exception("Can't create text_surface: "~ 1647 to!string(TTF_GetError())); 1648 } 1649 1650 Texture_Tick *ctt; 1651 if (!search_mode || hist_pos != 0) 1652 { 1653 ssize_t ipos = pos; 1654 if (ipos > command.length) ipos = command.length-1; 1655 complete = autocomplete(gs, command[0..ipos]); 1656 1657 if (complete.length > 1) 1658 { 1659 int linewidth = gs.screen.w-80-ptt.w-tt.w; 1660 if (complete[0] == '2') 1661 { 1662 linewidth = gs.screen.w-80; 1663 } 1664 1665 ctt = gs.text_viewer.font.get_line_from_cache(complete[1..$], 1666 9, linewidth, line_height, SDL_Color(0xFF, 0x96, 0x00, 0xFF)); 1667 if (!ctt && !ctt.texture) 1668 { 1669 throw new Exception("Can't create text_surface: "~ 1670 to!string(TTF_GetError())); 1671 } 1672 } 1673 } 1674 1675 auto lines = tt.h / line_height; 1676 y_off = gs.screen.h - line_height*3 - line_height*lines - 8; 1677 1678 /* EN: render background of console messages 1679 RU: рендерим фон консоли сообщений */ 1680 SDL_Rect rect; 1681 rect.x = 32; 1682 rect.y = cast(int)y_off; 1683 rect.w = gs.screen.w - 32*2; 1684 rect.h = cast(int)(line_height*lines + 8); 1685 1686 cmd_rect = rect; 1687 1688 if (complete.length > 1 && complete[0] == '2' && ctt !is null) 1689 { 1690 rect.y -= ctt.h; 1691 rect.h += ctt.h; 1692 } 1693 1694 r = SDL_RenderCopy(gs.renderer, gs.texture_black, null, &rect); 1695 if (r < 0) 1696 { 1697 writefln( "draw_command_line(), 8: Error while render copy: %s", 1698 SDL_GetError().to!string() ); 1699 } 1700 1701 /* EN: Render prompt to screen 1702 RU: Рендерим приглашение на экран */ 1703 auto i = 0; 1704 rect = SDL_Rect(); 1705 rect.x = 40; 1706 rect.y = cast(int)(y_off + 4 + line_height*i); 1707 rect.w = ptt.w; 1708 rect.h = ptt.h; 1709 1710 r = SDL_RenderCopy(gs.renderer, ptt.texture, null, &rect); 1711 if (r < 0) 1712 { 1713 writefln( 1714 "draw_command_line(), 9: Error while render copy: %s", 1715 SDL_GetError().to!string() ); 1716 } 1717 1718 /* EN: Render text to screen 1719 RU: Рендерим текст на экран */ 1720 rect = SDL_Rect(); 1721 rect.x = 40+ptt.w; 1722 rect.y = cast(int)(y_off + 4 + line_height*i); 1723 rect.w = tt.w; 1724 rect.h = tt.h; 1725 1726 cmd_mouse_pos = get_position_by_chars( 1727 gs.mouse_screen_x - rect.x, 1728 gs.mouse_screen_y - rect.y, tt.chars); 1729 1730 r = SDL_RenderCopy(gs.renderer, tt.texture, null, &rect); 1731 if (r < 0) 1732 { 1733 writefln( 1734 "draw_command_line(), 10: Error while render copy: %s", 1735 SDL_GetError().to!string() ); 1736 } 1737 1738 /* EN: Render autocomplete to screen 1739 RU: Рендерим автодополнение на экран */ 1740 if ((!search_mode || hist_pos != 0) && complete.length > 1) 1741 { 1742 rect = SDL_Rect(); 1743 if (complete[0] == '1' && pos == command.length) 1744 { 1745 rect.x = 40+ptt.w+tt.w-tt.chars[$-1].w; 1746 rect.y = cast(int)(y_off + 4 + line_height*i); 1747 rect.w = ctt.w; 1748 rect.h = ctt.h; 1749 } 1750 else if (complete[0] == '2' || pos < command.length) 1751 { 1752 rect.x = 40; 1753 rect.y = cast(int)(y_off + 4 + line_height*i - ctt.h); 1754 rect.w = ctt.w; 1755 rect.h = ctt.h; 1756 } 1757 1758 r = SDL_RenderCopy(gs.renderer, ctt.texture, null, &rect); 1759 if (r < 0) 1760 { 1761 writefln( 1762 "draw_command_line(), 10: Error while render copy: %s", 1763 SDL_GetError().to!string() ); 1764 } 1765 } 1766 1767 /* Render cursor */ 1768 if (pos < tt.chars.length) 1769 { 1770 rect = tt.chars[pos]; 1771 rect.x += 40+ptt.w; 1772 rect.y += cast(int)(y_off + 4 + line_height*i); 1773 string chr = " "; 1774 if (pos < str.length) 1775 chr = str[pos..pos+str.stride(pos)]; 1776 else if (complete.length > 1 && complete[0] == '1') 1777 chr = complete[1..1+complete.stride(1)]; 1778 if (chr == "\n") chr = " "; 1779 1780 r = SDL_RenderCopy(gs.renderer, gs.texture_cursor, null, &rect); 1781 if (r < 0) 1782 { 1783 writefln( "draw_command_line(), 11: Error while render copy: %s", 1784 SDL_GetError().to!string() ); 1785 } 1786 1787 auto st = gs.text_viewer.font.get_char_from_cache(chr, 9, SDL_Color(0x00, 0x00, 0x20, 0xFF)); 1788 if (!st) return; 1789 1790 r = SDL_RenderCopy(gs.renderer, st.texture, null, &rect); 1791 if (r < 0) 1792 { 1793 writefln( 1794 "draw_command_line(), 12: Error while render copy: %s", 1795 SDL_GetError().to!string() ); 1796 } 1797 } 1798 } 1799 1800 last_redraw = SDL_GetTicks(); 1801 1802 r = SDL_SetRenderTarget(gs.renderer, old_texture); 1803 if (r < 0) 1804 { 1805 throw new Exception(format("Error while restore render target: %s", 1806 fromStringz(SDL_GetError()) )); 1807 } 1808 1809 gs.text_viewer.font.clear_chars_cache(); 1810 gs.text_viewer.font.clear_lines_cache(); 1811 } 1812 1813 SDL_Rect dst = SDL_Rect(0, 0, gs.screen.w, gs.screen.h); 1814 int r = SDL_RenderCopy(gs.renderer, texture, null, &dst); 1815 if (r < 0) 1816 { 1817 writefln( "draw_text(): Error while render copy texture: %s", fromStringz(SDL_GetError()) ); 1818 } 1819 } 1820 1821 } 1822 1823 void update_winsize(GlobalState gs) 1824 { 1825 if (gs.command_line.fontsize < 5) return; 1826 1827 int line_height = cast(int)(round(SQRT2^^9)*1.2); 1828 auto h = gs.screen.h - line_height*2; 1829 auto w = gs.screen.w - 80; 1830 1831 auto st = gs.text_viewer.font.get_char_from_cache(" ", 1832 gs.command_line.fontsize, SDL_Color(0xFF, 0xFF, 0xFF, 0xFF)); 1833 if (!st) return; 1834 1835 with ( gs.command_line.ws) 1836 { 1837 ws_xpixel = cast(ushort)w; 1838 ws_ypixel = cast(ushort)h; 1839 ws_row = cast(ushort)(h/st.h); 1840 ws_col = cast(ushort)(w/st.w); 1841 } 1842 1843 foreach (tid; gs.tid_by_command_id) 1844 { 1845 tid.send(gs.command_line.ws); 1846 } 1847 } 1848 1849 1850 void 1851 hist_up(GlobalState gs) 1852 { 1853 with (gs.command_line) 1854 { 1855 cmd_start_selection = -1; 1856 cmd_end_selection = -1; 1857 1858 cwd = gs.current_path; 1859 Dbc cursor = gs.db_commands.cursor(null, 0); 1860 scope(exit) cursor.close(); 1861 1862 Dbt key, data; 1863 ulong id = find_prev_command(cursor, cwd, hist_cmd_id, 1864 key, data); 1865 1866 if (id != 0) 1867 { 1868 do 1869 { 1870 string key_string = key.to!(string); 1871 command_key cmd_key; 1872 parse_key_for_command(key_string, cmd_key); 1873 if (cwd != cmd_key.cwd) 1874 { 1875 id = 0; 1876 } 1877 else 1878 { 1879 string data_string = data.to!(string); 1880 command_data cmd_data; 1881 parse_data_for_command(data_string, cmd_data); 1882 1883 if (hist_pos == 0) 1884 { 1885 edited_command = command; 1886 } 1887 hist_pos++; 1888 1889 if (search_mode && cmd_data.command.indexOf(search) < 0) 1890 continue; 1891 1892 id = cmd_key.id; 1893 command = cmd_data.command.idup(); 1894 pos = command.length; 1895 //writefln("Excellent"); 1896 //writefln("cmd_data.command=%s", cmd_data.command); 1897 //writefln("pos=%s", pos); 1898 } 1899 break; 1900 } 1901 while (cursor.get(&key, &data, DB_PREV) == 0); 1902 } 1903 1904 if (id == 0) 1905 { 1906 hist_pos = 0; 1907 command = edited_command; 1908 pos = command.length; 1909 } 1910 1911 hist_cmd_id = id; 1912 } 1913 } 1914 1915 void 1916 hist_down(GlobalState gs) 1917 { 1918 with (gs.command_line) 1919 { 1920 cmd_start_selection = -1; 1921 cmd_end_selection = -1; 1922 1923 cwd = gs.current_path; 1924 Dbc cursor = gs.db_commands.cursor(null, 0); 1925 scope(exit) cursor.close(); 1926 1927 Dbt key, data; 1928 ulong id = find_next_command(cursor, cwd, hist_cmd_id, 1929 key, data); 1930 1931 if (id != 0) 1932 { 1933 do 1934 { 1935 string key_string = key.to!(string); 1936 command_key cmd_key; 1937 parse_key_for_command(key_string, cmd_key); 1938 if (cwd != cmd_key.cwd) 1939 { 1940 id = 0; 1941 } 1942 else 1943 { 1944 //writefln("Excellent"); 1945 string data_string = data.to!(string); 1946 command_data cmd_data; 1947 parse_data_for_command(data_string, cmd_data); 1948 1949 if (hist_pos == 0) 1950 { 1951 edited_command = command; 1952 } 1953 hist_pos++; 1954 1955 if (search_mode && cmd_data.command.indexOf(search) < 0) 1956 continue; 1957 1958 id = cmd_key.id; 1959 command = cmd_data.command.idup(); 1960 pos = command.length; 1961 } 1962 break; 1963 } 1964 while (cursor.get(&key, &data, DB_NEXT) == 0); 1965 } 1966 1967 if (id == 0) 1968 { 1969 hist_pos = 0; 1970 command = edited_command; 1971 pos = command.length; 1972 } 1973 1974 gs.command_line.hist_cmd_id = id; 1975 } 1976 }