1 module unde.lib; 2 3 import derelict.sdl2.sdl; 4 import std.container.dlist; 5 import std.stdio; 6 import std.format; 7 import std..string; 8 import std.process; 9 import std.regex; 10 import std.conv; 11 import std.utf; 12 import core.exception; 13 14 import derelict.sdl2.sdl; 15 16 import unde.global_state; 17 import unde.slash; 18 19 import core.sys.posix.sys.stat; 20 import core.sys.posix.pwd; 21 import core.sys.posix.grp; 22 version(Windows) 23 { 24 import core.stdc.time; 25 alias ulong ulong_t; 26 } 27 28 enum DOUBLE_DELAY=750; 29 30 enum PATH_MAX=4096; //from linux/limits.h 31 enum UUID_MAX=36; 32 enum MARKS_PATH_MAX=PATH_MAX+UUID_MAX; 33 34 struct Gradient 35 { 36 struct ColorPoint 37 { 38 SDL_Color color; 39 double point; 40 } 41 42 DList!ColorPoint grad; 43 44 void add(SDL_Color color, double point) 45 { 46 auto range = grad[]; 47 48 foreach(a; grad[]) 49 { 50 if (point < a.point) 51 { 52 grad.insertBefore(range, ColorPoint(color, point)); 53 return; 54 } 55 range.popFront(); 56 } 57 grad.insertBack(ColorPoint(color, point)); 58 } 59 60 SDL_Color getColor(double point) 61 { 62 auto range = grad[]; 63 64 ColorPoint *a; 65 ColorPoint *b; 66 67 foreach(g; grad[]) 68 { 69 if (point < g.point) 70 { 71 b = &g; 72 if (!a) return b.color; 73 74 SDL_Color ret; 75 double k = (point - a.point) / (b.point - a.point); 76 ret.r = cast(ubyte)(a.color.r * (1.0-k) + b.color.r * k); 77 ret.g = cast(ubyte)(a.color.g * (1.0-k) + b.color.g * k); 78 ret.b = cast(ubyte)(a.color.b * (1.0-k) + b.color.b * k); 79 ret.a = cast(ubyte)(a.color.a * (1.0-k) + b.color.a * k); 80 return ret; 81 } 82 a = &range.front(); 83 range.popFront(); 84 } 85 86 return grad[].back.color; 87 } 88 } 89 90 unittest 91 { 92 import std.algorithm: equal; 93 Gradient grad; 94 grad.add(SDL_Color(0, 0, 0, 1), 1); 95 grad.add(SDL_Color(0, 0, 0, 3), 3); 96 grad.add(SDL_Color(0, 0, 0, 2), 2); 97 grad.add(SDL_Color(0, 0, 0, 0), 0); 98 99 /*foreach(a; grad.grad[]) 100 { 101 writefln("%d - %.2f", a.color.a, a.point); 102 }*/ 103 assert(equal(grad.grad[], [ 104 Gradient.ColorPoint(SDL_Color(0,0,0,0), 0), 105 Gradient.ColorPoint(SDL_Color(0,0,0,1), 1), 106 Gradient.ColorPoint(SDL_Color(0,0,0,2), 2), 107 Gradient.ColorPoint(SDL_Color(0,0,0,3), 3),])); 108 } 109 110 struct DRect 111 { 112 double x,y,w,h; 113 114 bool In(in ref DRect b) const 115 { 116 return x >= b.x && (x+w) <= (b.x+b.w) && 117 y >= b.y && (y+h) <= (b.y+b.h); 118 } 119 bool NotIntersect(in ref DRect b) const 120 { 121 return ((b.x+b.w) < x || b.x > (x+w) || 122 (b.y+b.h) < y || b.y > (y+h)); 123 } 124 DRect apply(in ref DRect b) const 125 { 126 DRect res; 127 res.x = b.x + x*b.w/(1024*1024); 128 res.y = b.y + y*b.h/(1024*1024); 129 res.w = b.w * w / (1024*1024); 130 res.h = b.h * h / (1024*1024); 131 //assert(res.In(b), format("apply must make rectangle which lays in b.\nthis=%s, b=%s, res=%s", this, b, res)); 132 return res; 133 } 134 SDL_Rect to_screen(in ref CoordinatesPlusScale screen) 135 { 136 SDL_Rect rect; 137 rect.x = cast(int)((x - screen.x)/screen.scale); 138 rect.y = cast(int)((y - screen.y)/screen.scale); 139 rect.w = cast(int)(w/screen.scale); 140 rect.h = cast(int)(h/screen.scale); 141 return rect; 142 } 143 144 void rescale_screen(ref CoordinatesPlusScale screen, SDL_Rect rect) 145 { 146 screen.scale = w/rect.w; 147 screen.x = x - rect.x*screen.scale; 148 screen.y = y - rect.y*screen.scale; 149 } 150 } 151 152 struct CoordinatesPlusScale 153 { 154 double x, y; 155 int w, h; 156 double scale; 157 158 DRect getRect() 159 { 160 DRect rect; 161 rect.x = x; 162 rect.y = y; 163 rect.w = w*scale; 164 rect.h = h*scale; 165 return rect; 166 } 167 } 168 169 enum SortType 170 { 171 ByName = 0, 172 BySize, 173 ByTime 174 } 175 176 enum FileType 177 { 178 Directory = 0, 179 File, 180 Image, 181 Text 182 } 183 184 enum InfoType 185 { 186 None = 0, 187 CreateDirectory, 188 Copy, 189 Move, 190 FileInfo, 191 Progress 192 } 193 194 char[i] to_char_array(int i)(string str) 195 { 196 char[i] ret; 197 size_t l = str.length; 198 if (l > i) l = i; 199 ret[0..l] = str[0..l]; 200 return ret; 201 } 202 203 char[i] to_char_array_z(int i)(string str) 204 { 205 char[i] ret; 206 size_t l = str.length; 207 if (l > i) l = i; 208 ret[0..l] = str[0..l]; 209 ret[l..$] = '\0'; 210 return ret; 211 } 212 213 string from_char_array(const char[] str) 214 { 215 int i; 216 foreach (c; str) 217 { 218 if (c == char.init) break; 219 i++; 220 } 221 return str[0..i].idup(); 222 } 223 224 wstring from_char_array(const wchar[] str) 225 { 226 int i; 227 foreach (c; str) 228 { 229 if (c == wchar.init) break; 230 i++; 231 } 232 return str[0..i].idup(); 233 } 234 235 string strip_error(string str) 236 { 237 return str[str.indexOf(":")+2..$]; 238 } 239 240 struct RectSize 241 { 242 DRect rect_by_name; 243 DRect rect_by_size; 244 DRect rect_by_time; 245 246 long size; 247 long disk_usage; 248 time_t mtime; 249 ulong_t mtime_nsec; 250 long files; 251 InfoType show_info; 252 253 union{ 254 struct 255 { 256 // Error Message 257 char[80] msg; 258 long msg_time; 259 uint msg_color; 260 }; 261 struct 262 { 263 // Progress Info 264 char[80] path; 265 long estimate_end; 266 int progress; // from 0 tlll 10000 267 }; 268 } 269 270 FileType type; 271 272 union 273 { 274 struct { 275 // Directory 276 SortType sort; 277 long newest_msg_time; 278 }; 279 struct { 280 // Image 281 double angle; 282 }; 283 struct { 284 // Text 285 long offset; 286 char[32] charset; 287 }; 288 } 289 290 ref inout(DRect) rect(const SortType sort) inout 291 { 292 final switch(sort) 293 { 294 case SortType.ByName: 295 return rect_by_name; 296 case SortType.BySize: 297 return rect_by_size; 298 case SortType.ByTime: 299 return rect_by_time; 300 } 301 } 302 } 303 304 struct Mark 305 { 306 char[MARKS_PATH_MAX] path; 307 SDL_Rect screen_rect; 308 State state; 309 union{ 310 struct 311 { // Text 312 long offset; 313 } 314 } 315 } 316 317 struct DirEntryRect 318 { 319 string path; 320 DRect rect; 321 long size; 322 alias rect this; 323 324 this (string path, long x, long y, long w, long h, long size) 325 { 326 this.path = path; 327 rect = DRect(x, y, w, h); 328 this.size = size; 329 } 330 331 this (string path, DRect rect, long size) 332 { 333 this.path = path; 334 this.rect = rect; 335 this.size = size; 336 } 337 } 338 339 string 340 subpath(string path, string mnt) 341 { 342 if (mnt == SL) return path; 343 if (mnt == path) return SL; 344 return path[mnt.length..$]; 345 } 346 347 void 348 calculate_selection_sub(GlobalState gs) 349 { 350 gs.selection_sub = null; 351 foreach(path, rectsize; gs.selection_hash) 352 { 353 path = getParent(path); 354 while (path > "") 355 { 356 gs.selection_sub[path] = true; 357 path = getParent(path); 358 } 359 gs.selection_sub[SL] = true; 360 } 361 } 362 363 struct Selection 364 { 365 string from; 366 string to; 367 368 long size_from; 369 long size_to; 370 371 time_t mtime_from; 372 time_t mtime_to; 373 374 SortType sort; 375 } 376 377 version (Posix) 378 { 379 import core.sys.posix.sys.types; 380 import core.sys.posix.sys.stat; 381 string mode_to_string(mode_t mode) 382 { 383 char[12] res; 384 res[ 0] = mode & S_ISUID ? 'U' : '-'; 385 res[ 1] = mode & S_ISGID ? 'G' : '-'; 386 res[ 2] = mode & S_ISVTX ? 'S' : '-'; 387 res[ 3] = mode & S_IRUSR ? 'r' : '-'; 388 res[ 4] = mode & S_IWUSR ? 'w' : '-'; 389 res[ 5] = mode & S_IXUSR ? 'x' : '-'; 390 res[ 6] = mode & S_IRGRP ? 'r' : '-'; 391 res[ 7] = mode & S_IWGRP ? 'w' : '-'; 392 res[ 8] = mode & S_IXGRP ? 'x' : '-'; 393 res[ 9] = mode & S_IROTH ? 'r' : '-'; 394 res[10] = mode & S_IWOTH ? 'w' : '-'; 395 res[11] = mode & S_IXOTH ? 'x' : '-'; 396 return res.idup(); 397 } 398 399 string uid_to_name(uid_t uid) 400 { 401 auto pwd = getpwuid(uid); 402 return fromStringz(pwd.pw_name).idup(); 403 } 404 405 string gid_to_name(gid_t gid) 406 { 407 auto pwd = getgrgid(gid); 408 return fromStringz(pwd.gr_name).idup(); 409 } 410 } 411 412 string 413 mime(string path) 414 { 415 auto df_pipes = pipeProcess(["file", "-bi", path], Redirect.stdout); 416 scope(exit) wait(df_pipes.pid); 417 418 int l=0; 419 foreach (df_line; df_pipes.stdout.byLine) 420 { 421 if (l == 0) 422 { 423 auto match = matchFirst(df_line, regex(`(.*); charset=.*`)); 424 if (match) 425 { 426 return match[1].idup(); 427 } 428 } 429 l++; 430 } 431 return "error/none"; 432 } 433 434 int 435 db_recover(string path) 436 { 437 auto df_pipes = pipeProcess(["db_recover", "-h", path], Redirect.stdout | Redirect.stderrToStdout); 438 scope(exit) wait(df_pipes.pid); 439 440 foreach (df_line; df_pipes.stdout.byLine) 441 { 442 writefln(df_line); 443 } 444 445 return 0; 446 } 447 448 void 449 unDE_RenderFillRect(SDL_Renderer *renderer, SDL_Rect *rect, uint color) 450 { 451 int r = SDL_SetRenderDrawColor(renderer, 452 (color&0xFF0000) >> 16, 453 (color&0xFF00) >> 8, 454 color&0xFF, 455 (color&0xFF000000) >> 24); 456 if (r < 0) 457 { 458 writefln("Can't SDL_SetRenderDrawColor: %s", 459 fromStringz(SDL_GetError())); 460 } 461 462 r = SDL_RenderFillRect(renderer, rect); 463 if (r < 0) 464 { 465 writefln("Can't SDL_RenderFillRect: %s", 466 fromStringz(SDL_GetError())); 467 } 468 } 469 470 version(Windows) 471 { 472 import core.sys.windows.winbase; 473 474 string GetErrorMessage() 475 { 476 wchar[1024] pBuffer; 477 478 auto res = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 479 null, 480 GetLastError(), 481 0, 482 pBuffer.ptr, 483 pBuffer.length, 484 null); 485 if (!res) 486 { 487 writefln("FormatMessage error %s", GetLastError()); 488 } 489 490 return to!string(from_char_array(pBuffer)); 491 } 492 } 493 494 string getParent(string path) 495 { 496 version (Windows) 497 { 498 if (path.lastIndexOf(SL) < 0) 499 return SL; 500 } 501 return path[0..path.lastIndexOf(SL)]; 502 } 503 504 size_t 505 mystride(T)(ref T str, size_t pos, size_t len = 0) 506 { 507 /* stride falls with OutOfMemoryError Sometimes 508 on not correct symbols, so we will use out stride */ 509 510 if ((str[pos] & 0b1000_0000) == 0) 511 return 1; 512 513 if (len == 0) len = str.length; 514 515 size_t i; 516 for (i=pos+1; i < len && (str[i] & 0b1100_0000) == 0b1000_0000; i++) 517 { 518 } 519 return i-pos; 520 } 521 522 size_t 523 mystrideBack(T)(ref T str, size_t pos) 524 { 525 try 526 { 527 return str.strideBack(pos); 528 } 529 catch (UnicodeException e) 530 { 531 } 532 catch (UTFException e) 533 { 534 } 535 catch (OutOfMemoryError e) 536 { 537 } 538 return 1; 539 } 540 541 size_t 542 myWalkLength(char[] str) 543 { 544 size_t n = 0; 545 for (size_t i = 0; i < str.length; i+=mystride(str, i)) 546 n++; 547 return n; 548 } 549 550 // TODO Move to unde.file_manager.lib 551 import unde.viewers.image_viewer.lib; 552 import unde.viewers.text_viewer.lib; 553 import unde.path_mnt; 554 void 555 openFile(GlobalState gs, PathMnt path) 556 { 557 string mime_type = mime(path); 558 if (mime_type == "inode/directory" || 559 mime_type == "error/none") 560 { 561 // Do nothing 562 } 563 else if (mime_type.startsWith("image/")) 564 { 565 image_viewer(gs, path); 566 } 567 else if (mime_type.startsWith("text/")) 568 { 569 text_viewer(gs, path); 570 } 571 else 572 { 573 string msg = format("No viewer for '%s' mime type", mime_type); 574 gs.messages ~= ConsoleMessage( 575 SDL_Color(0xFF, 0x00, 0x00, 0xFF), 576 msg, 577 SDL_GetTicks() 578 ); 579 writeln(msg); 580 } 581 } 582 583 // TODO Move to unde.file_manager.lib 584 void 585 openFileByMime(GlobalState gs, string path) 586 { 587 string mime_type = mime(path); 588 if ( mime_type in gs.mime_applications ) 589 { 590 auto pid = spawnProcess([gs.mime_applications[mime_type], path]); 591 gs.pids ~= pid; 592 } 593 else 594 { 595 string msg = format("No mime application for '%s' mime type in ~/.unde/mime", mime_type); 596 gs.messages ~= ConsoleMessage( 597 SDL_Color(0xFF, 0x00, 0x00, 0xFF), 598 msg, 599 SDL_GetTicks() 600 ); 601 writeln(msg); 602 } 603 } 604 605 static byte[byte] clear_check; 606 607 static if (!__traits(hasMember, clear_check, "clear")) 608 { 609 void clear(A, B) (A[B] a) 610 { 611 foreach (key; a.keys) 612 { 613 a.remove(key); 614 } 615 } 616 }