1 // Written by Christopher E. Miller 2 // See the included license.txt for copyright and license details. 3 4 5 // Not actually part of forms, but is handy. 6 7 /// 8 module dfl.environment; 9 10 private import dfl.internal.dlib, dfl.internal.clib; 11 12 private import dfl.internal.winapi, dfl.base, dfl.internal.utf, dfl.event; 13 14 15 /// 16 final class Environment // docmain 17 { 18 private this() {} 19 20 21 static: 22 23 /// 24 @property Dstring commandLine() // getter 25 { 26 return dfl.internal.utf.getCommandLine(); 27 } 28 29 30 /// 31 @property void currentDirectory(Dstring cd) // setter 32 { 33 if(!dfl.internal.utf.setCurrentDirectory(cd)) 34 throw new DflException("Unable to set current directory"); 35 } 36 37 /// ditto 38 @property Dstring currentDirectory() // getter 39 { 40 return dfl.internal.utf.getCurrentDirectory(); 41 } 42 43 44 /// 45 @property Dstring machineName() // getter 46 { 47 Dstring result; 48 result = dfl.internal.utf.getComputerName(); 49 if(!result.length) 50 throw new DflException("Unable to obtain machine name"); 51 return result; 52 } 53 54 55 /// 56 @property Dstring newLine() // getter 57 { 58 return nativeLineSeparatorString; 59 } 60 61 62 /// 63 @property OperatingSystem osVersion() // getter 64 { 65 OSVERSIONINFOA osi; 66 Version ver; 67 68 osi.dwOSVersionInfoSize = osi.sizeof; 69 if(!GetVersionExA(&osi)) 70 throw new DflException("Unable to obtain operating system version information"); 71 72 int build; 73 74 switch(osi.dwPlatformId) 75 { 76 case VER_PLATFORM_WIN32_NT: 77 ver = new Version(osi.dwMajorVersion, osi.dwMinorVersion, osi.dwBuildNumber); 78 break; 79 80 case VER_PLATFORM_WIN32_WINDOWS: 81 ver = new Version(osi.dwMajorVersion, osi.dwMinorVersion, LOWORD(osi.dwBuildNumber)); 82 break; 83 case VER_PLATFORM_X64_WINDOWS: 84 ver = new Version(osi.dwMajorVersion, osi.dwMinorVersion, LOWORD(osi.dwBuildNumber)); 85 break; 86 default: 87 ver = new Version(osi.dwMajorVersion, osi.dwMinorVersion); 88 } 89 90 return new OperatingSystem(cast(PlatformId)osi.dwPlatformId, ver); 91 } 92 93 94 /// 95 @property Dstring systemDirectory() // getter 96 { 97 Dstring result; 98 result = dfl.internal.utf.getSystemDirectory(); 99 if(!result.length) 100 throw new DflException("Unable to obtain system directory"); 101 return result; 102 } 103 104 105 // Should return int ? 106 @property DWORD tickCount() // getter 107 { 108 return GetTickCount(); 109 } 110 111 112 /// 113 @property Dstring userName() // getter 114 { 115 Dstring result; 116 result = dfl.internal.utf.getUserName(); 117 if(!result.length) 118 throw new DflException("Unable to obtain user name"); 119 return result; 120 } 121 122 123 /// 124 void exit(int code) 125 { 126 // This is probably better than ExitProcess(code). 127 dfl.internal.clib.exit(code); 128 } 129 130 131 /// 132 Dstring expandEnvironmentVariables(Dstring str) 133 { 134 if(!str.length) 135 { 136 return str; 137 } 138 Dstring result; 139 if(!dfl.internal.utf.expandEnvironmentStrings(str, result)) 140 throw new DflException("Unable to expand environment variables"); 141 return result; 142 } 143 144 145 /// 146 Dstring[] getCommandLineArgs() 147 { 148 return parseArgs(commandLine); 149 } 150 151 152 /// 153 Dstring getEnvironmentVariable(Dstring name, bool throwIfMissing) 154 { 155 Dstring result; 156 result = dfl.internal.utf.getEnvironmentVariable(name); 157 if(!result.length) 158 { 159 if(!throwIfMissing) 160 { 161 if(GetLastError() == 203) // ERROR_ENVVAR_NOT_FOUND 162 return null; 163 } 164 throw new DflException("Unable to obtain environment variable"); 165 } 166 return result; 167 } 168 169 /// ditto 170 Dstring getEnvironmentVariable(Dstring name) 171 { 172 return getEnvironmentVariable(name, true); 173 } 174 175 176 //Dstring[Dstring] getEnvironmentVariables() 177 //Dstring[] getEnvironmentVariables() 178 179 180 /// 181 Dstring[] getLogicalDrives() 182 { 183 DWORD dr = GetLogicalDrives(); 184 Dstring[] result; 185 int i; 186 char[4] tmp = " :\\\0"; 187 188 for(i = 0; dr; i++) 189 { 190 if(dr & 1) 191 { 192 char[] s = tmp.dup[0 .. 3]; 193 s[0] = cast(char)('A' + i); 194 //result ~= s; 195 result ~= cast(Dstring)s; // Needed in D2. 196 } 197 dr >>= 1; 198 } 199 200 return result; 201 } 202 } 203 204 205 /+ 206 enum PowerModes: ubyte 207 { 208 STATUS_CHANGE, 209 RESUME, 210 SUSPEND, 211 } 212 213 214 class PowerModeChangedEventArgs: EventArgs 215 { 216 this(PowerModes pm) 217 { 218 this._pm = pm; 219 } 220 221 222 @property final PowerModes mode() // getter 223 { 224 return _pm; 225 } 226 227 228 private: 229 PowerModes _pm; 230 } 231 +/ 232 233 234 /+ 235 /// 236 enum SessionEndReasons: ubyte 237 { 238 SYSTEM_SHUTDOWN, /// 239 LOGOFF, /// ditto 240 } 241 242 243 /// 244 class SystemEndedEventArgs: EventArgs 245 { 246 /// 247 this(SessionEndReasons reason) 248 { 249 this._reason = reason; 250 } 251 252 253 /// 254 final @property SessionEndReasons reason() // getter 255 { 256 return this._reason; 257 } 258 259 260 private: 261 SessionEndReasons _reason; 262 } 263 264 265 /// 266 class SessionEndingEventArgs: EventArgs 267 { 268 /// 269 this(SessionEndReasons reason) 270 { 271 this._reason = reason; 272 } 273 274 275 /// 276 final @property SessionEndReasons reason() // getter 277 { 278 return this._reason; 279 } 280 281 282 /// 283 final @property void cancel(bool byes) // setter 284 { 285 this._cancel = byes; 286 } 287 288 /// ditto 289 final @property bool cancel() // getter 290 { 291 return this._cancel; 292 } 293 294 295 private: 296 SessionEndReasons _reason; 297 bool _cancel = false; 298 } 299 +/ 300 301 302 /+ 303 final class SystemEvents // docmain 304 { 305 private this() {} 306 307 308 static: 309 EventHandler displaySettingsChanged; 310 EventHandler installedFontsChanged; 311 EventHandler lowMemory; // GC automatically collects before this event. 312 EventHandler paletteChanged; 313 //PowerModeChangedEventHandler powerModeChanged; // WM_POWERBROADCAST 314 SystemEndedEventHandler systemEnded; 315 SessionEndingEventHandler systemEnding; 316 SessionEndingEventHandler sessionEnding; 317 EventHandler timeChanged; 318 // user preference changing/changed. WM_SETTINGCHANGE ? 319 320 321 /+ 322 @property void useOwnThread(bool byes) // setter 323 { 324 if(byes != useOwnThread) 325 { 326 if(byes) 327 { 328 _ownthread = new Thread; 329 // idle priority.. 330 } 331 else 332 { 333 // Kill thread. 334 } 335 } 336 } 337 338 339 @property bool useOwnThread() // getter 340 { 341 return _ownthread !is null; 342 } 343 +/ 344 345 346 private: 347 //package Thread _ownthread = null; 348 349 350 SessionEndReasons sessionEndReasonFromLparam(LPARAM lparam) 351 { 352 if(ENDSESSION_LOGOFF == lparam) 353 return SessionEndReasons.LOGOFF; 354 return SessionEndReasons.SYSTEM_SHUTDOWN; 355 } 356 357 358 void _realCheckMessage(ref Message m) 359 { 360 switch(m.msg) 361 { 362 case WM_DISPLAYCHANGE: 363 displaySettingsChanged(typeid(SystemEvents), EventArgs.empty); 364 break; 365 366 case WM_FONTCHANGE: 367 installedFontsChanged(typeid(SystemEvents), EventArgs.empty); 368 break; 369 370 case WM_COMPACTING: 371 //gcFullCollect(); 372 lowMemory(typeid(SystemEvents), EventArgs.empty); 373 break; 374 375 case WM_PALETTECHANGED: 376 paletteChanged(typeid(SystemEvents), EventArgs.empty); 377 break; 378 379 case WM_ENDSESSION: 380 if(m.wParam) 381 { 382 scope SystemEndedEventArgs ea = new SystemEndedEventArgs(sessionEndReasonFromLparam(m.lParam)); 383 systemEnded(typeid(SystemEvents), ea); 384 } 385 break; 386 387 case WM_QUERYENDSESSION: 388 { 389 scope SessionEndingEventArgs ea = new SessionEndingEventArgs(sessionEndReasonFromLparam(m.lParam)); 390 systemEnding(typeid(SystemEvents), ea); 391 if(ea.cancel) 392 m.result = FALSE; // Stop shutdown. 393 m.result = TRUE; // Continue shutdown. 394 } 395 break; 396 397 case WM_TIMECHANGE: 398 timeChanged(typeid(SystemEvents), EventArgs.empty); 399 break; 400 401 default: 402 } 403 } 404 405 406 package void _checkMessage(ref Message m) 407 { 408 //if(_ownthread) 409 _realCheckMessage(m); 410 } 411 } 412 +/ 413 414 415 package Dstring[] parseArgs(Dstring args) 416 { 417 Dstring[] result; 418 uint i; 419 bool inQuote = false; 420 bool findStart = true; 421 uint startIndex = 0; 422 423 for(i = 0;; i++) 424 { 425 if(i == args.length) 426 { 427 if(findStart) 428 startIndex = i; 429 break; 430 } 431 432 if(findStart) 433 { 434 if(args[i] == ' ' || args[i] == '\t') 435 continue; 436 findStart = false; 437 startIndex = i; 438 } 439 440 if(args[i] == '"') 441 { 442 inQuote = !inQuote; 443 if(!inQuote) //matched quotes 444 { 445 result.length = result.length + 1; 446 result[result.length - 1] = args[startIndex .. i]; 447 findStart = true; 448 } 449 else //starting quote 450 { 451 if(startIndex != i) //must be a quote stuck to another word, separate them 452 { 453 result.length = result.length + 1; 454 result[result.length - 1] = args[startIndex .. i]; 455 startIndex = i + 1; 456 } 457 else 458 { 459 startIndex++; //exclude the quote 460 } 461 } 462 } 463 else if(!inQuote) 464 { 465 if(args[i] == ' ' || args[i] == '\t') 466 { 467 result.length = result.length + 1; 468 result[result.length - 1] = args[startIndex .. i]; 469 findStart = true; 470 } 471 } 472 } 473 474 if(startIndex != i) 475 { 476 result.length = result.length + 1; 477 result[result.length - 1] = args[startIndex .. i]; 478 } 479 480 return result; 481 } 482 483 484 unittest 485 { 486 Dstring[] args; 487 488 args = parseArgs(`"foo" bar`); 489 assert(args.length == 2); 490 assert(args[0] == "foo"); 491 assert(args[1] == "bar"); 492 493 args = parseArgs(`"environment"`); 494 assert(args.length == 1); 495 assert(args[0] == "environment"); 496 497 /+ 498 writefln("commandLine = '%s'", Environment.commandLine); 499 foreach(Dstring arg; Environment.getCommandLineArgs()) 500 { 501 writefln("\t'%s'", arg); 502 } 503 +/ 504 } 505 506 507 /// 508 // Any version, not just the operating system. 509 class Version // docmain ? 510 { 511 private: 512 int _major = 0, _minor = 0; 513 int _build = -1, _revision = -1; 514 515 516 public: 517 518 /// 519 this() 520 { 521 } 522 523 524 final: 525 526 /// ditto 527 // A string containing "major.minor.build.revision". 528 // 2 to 4 parts expected. 529 this(Dstring str) 530 { 531 Dstring[] stuff = stringSplit(str, "."); 532 533 switch(stuff.length) 534 { 535 case 4: 536 _revision = stringToInt(stuff[3]); 537 goto case 3; 538 case 3: 539 _build = stringToInt(stuff[2]); 540 goto case 2; 541 case 2: 542 _minor = stringToInt(stuff[1]); 543 _major = stringToInt(stuff[0]); 544 break; 545 default: 546 throw new DflException("Invalid version parameter"); 547 } 548 } 549 550 /// ditto 551 this(int major, int minor) 552 { 553 _major = major; 554 _minor = minor; 555 } 556 557 /// ditto 558 this(int major, int minor, int build) 559 { 560 _major = major; 561 _minor = minor; 562 _build = build; 563 } 564 565 /// ditto 566 this(int major, int minor, int build, int revision) 567 { 568 _major = major; 569 _minor = minor; 570 _build = build; 571 _revision = revision; 572 } 573 574 575 /+ // D2 doesn't like this without () but this invariant doesn't really even matter. 576 invariant 577 { 578 assert(_major >= 0); 579 assert(_minor >= 0); 580 assert(_build >= -1); 581 assert(_revision >= -1); 582 } 583 +/ 584 585 586 /// 587 override Dstring toString() 588 { 589 Dstring result; 590 591 result = intToString(_major) ~ "." ~ intToString(_minor); 592 if(_build != -1) 593 result ~= "." ~ intToString(_build); 594 if(_revision != -1) 595 result ~= "." ~ intToString(_revision); 596 597 return result; 598 } 599 600 601 /// 602 @property int major() // getter 603 { 604 return _major; 605 } 606 607 /// ditto 608 @property int minor() // getter 609 { 610 return _minor; 611 } 612 613 /// ditto 614 // -1 if no build. 615 @property int build() // getter 616 { 617 return _build; 618 } 619 620 /// ditto 621 // -1 if no revision. 622 @property int revision() // getter 623 { 624 return _revision; 625 } 626 } 627 628 629 /// 630 enum PlatformId: DWORD 631 { 632 WIN_CE = cast(DWORD)-1, 633 WIN32s = VER_PLATFORM_WIN32s, 634 WIN32_WINDOWS = VER_PLATFORM_WIN32_WINDOWS, 635 WIN32_NT = VER_PLATFORM_WIN32_NT, 636 WIN64_WINDOWS = VER_PLATFORM_X64_WINDOWS, 637 } 638 639 640 /// 641 final class OperatingSystem // docmain 642 { 643 final 644 { 645 /// 646 this(PlatformId platId, Version ver) 647 { 648 this.platId = platId; 649 this.vers = ver; 650 } 651 652 653 /// 654 override Dstring toString() 655 { 656 Dstring result; 657 658 // DMD 0.92 says error: cannot implicitly convert uint to PlatformId 659 switch(cast(DWORD)platId) 660 { 661 case PlatformId.WIN64_WINDOWS: 662 result = "Microsoft Windows 7 "; 663 break; 664 case PlatformId.WIN32_NT: 665 result = "Microsoft Windows NT "; 666 break; 667 668 case PlatformId.WIN32_WINDOWS: 669 result = "Microsoft Windows 95 "; 670 break; 671 672 case PlatformId.WIN32s: 673 result = "Microsoft Win32s "; 674 break; 675 676 case PlatformId.WIN_CE: 677 result = "Microsoft Windows CE "; 678 break; 679 680 681 default: 682 throw new DflException("Unknown platform ID"); 683 } 684 685 result ~= vers.toString(); 686 return result; 687 } 688 689 690 /// 691 @property PlatformId platform() // getter 692 { 693 return platId; 694 } 695 696 697 /// 698 // Should be version() :p 699 @property Version ver() // getter 700 { 701 return vers; 702 } 703 } 704 705 706 private: 707 PlatformId platId; 708 Version vers; 709 } 710