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