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