Mercurial > dwindows
comparison dwtestoo.cpp @ 2933:3cdb02171b01
C++: Implement Thread class and add the last page Thread/Event.
author | bsmith@81767d24-ef19-dc11-ae90-00e081727c95 |
---|---|
date | Sat, 31 Dec 2022 00:41:28 +0000 |
parents | 3f660f47a45f |
children | 6d3da42f63af |
comparison
equal
deleted
inserted
replaced
2932:3f660f47a45f | 2933:3cdb02171b01 |
---|---|
30 | 30 |
31 #define APP_TITLE "Dynamic Windows C++" | 31 #define APP_TITLE "Dynamic Windows C++" |
32 #define APP_EXIT "Are you sure you want to exit?" | 32 #define APP_EXIT "Are you sure you want to exit?" |
33 | 33 |
34 #define MAX_WIDGETS 20 | 34 #define MAX_WIDGETS 20 |
35 #define BUF_SIZE 1024 | |
35 | 36 |
36 // Handle the case of very old compilers by using | 37 // Handle the case of very old compilers by using |
37 // A simple non-lambda example instead. | 38 // A simple non-lambda example instead. |
38 #ifndef DW_LAMBDA | 39 #ifndef DW_LAMBDA |
39 | 40 |
464 if(fontname) | 465 if(fontname) |
465 snprintf(font, 100, "%d.%s", fontsize, fontname); | 466 snprintf(font, 100, "%d.%s", fontsize, fontname); |
466 mle->SetFont(fontname ? font : NULL); | 467 mle->SetFont(fontname ? font : NULL); |
467 } | 468 } |
468 | 469 |
470 // Thread and Event functions | |
471 void UpdateMLE(DW::MLE *threadmle, char *text, DW::Mutex *mutex) | |
472 { | |
473 static unsigned int pos = 0; | |
474 | |
475 // Protect pos from being changed by different threads | |
476 if(mutex) | |
477 mutex->Lock(); | |
478 pos = threadmle->Import(text, pos); | |
479 threadmle->SetCursor(pos); | |
480 if(mutex) | |
481 mutex->Unlock(); | |
482 } | |
483 | |
484 void RunThread(int threadnum, DW::Mutex *mutex, DW::Event *controlevent, DW::Event *workevent, DW::MLE *threadmle) { | |
485 char buf[BUF_SIZE+1] = {0}; | |
486 | |
487 snprintf(buf, BUF_SIZE, "Thread %d started.\r\n", threadnum); | |
488 UpdateMLE(threadmle, buf, mutex); | |
489 | |
490 // Increment the ready count while protected by mutex | |
491 dw_mutex_lock(mutex); | |
492 ready++; | |
493 // If all 4 threads have incrememted the ready count... | |
494 // Post the control event semaphore so things will get started. | |
495 if(ready == 4) | |
496 controlevent->Post(); | |
497 mutex->Unlock(); | |
498 | |
499 while(!finished) | |
500 { | |
501 int result = workevent->Wait(2000); | |
502 | |
503 if(result == DW_ERROR_TIMEOUT) | |
504 { | |
505 snprintf(buf, BUF_SIZE, "Thread %d timeout waiting for event.\r\n", threadnum); | |
506 UpdateMLE(threadmle, buf, mutex); | |
507 } | |
508 else if(result == DW_ERROR_NONE) | |
509 { | |
510 snprintf(buf, BUF_SIZE, "Thread %d doing some work.\r\n", threadnum); | |
511 UpdateMLE(threadmle, buf, mutex); | |
512 // Pretend to do some work | |
513 app->MainSleep(1000 * threadnum); | |
514 | |
515 // Increment the ready count while protected by mutex | |
516 mutex->Lock(); | |
517 ready++; | |
518 sprintf(buf, "Thread %d work done. ready=%d", threadnum, ready); | |
519 // If all 4 threads have incrememted the ready count... | |
520 // Post the control event semaphore so things will get started. | |
521 if(ready == 4) | |
522 { | |
523 controlevent->Post(); | |
524 strcat(buf, " Control posted."); | |
525 } | |
526 mutex->Unlock(); | |
527 strcat(buf, "\r\n"); | |
528 UpdateMLE(threadmle, buf, mutex); | |
529 } | |
530 else | |
531 { | |
532 snprintf(buf, BUF_SIZE, "Thread %d error %d.\r\n", threadnum, result); | |
533 UpdateMLE(threadmle, buf, mutex); | |
534 app->MainSleep(10000); | |
535 } | |
536 } | |
537 snprintf(buf, BUF_SIZE, "Thread %d finished.\r\n", threadnum); | |
538 UpdateMLE(threadmle, buf, mutex); | |
539 } | |
540 | |
541 void ControlThread(DW::Mutex *mutex, DW::Event *controlevent, DW::Event *workevent, DW::MLE *threadmle) { | |
542 int inprogress = 5; | |
543 char buf[BUF_SIZE+1] = {0}; | |
544 | |
545 while(inprogress) | |
546 { | |
547 int result = controlevent->Wait(2000); | |
548 | |
549 if(result == DW_ERROR_TIMEOUT) | |
550 { | |
551 UpdateMLE(threadmle, "Control thread timeout waiting for event.\r\n", mutex); | |
552 } | |
553 else if(result == DW_ERROR_NONE) | |
554 { | |
555 // Reset the control event | |
556 controlevent->Reset(); | |
557 ready = 0; | |
558 snprintf(buf, BUF_SIZE, "Control thread starting worker threads. Inprogress=%d\r\n", inprogress); | |
559 UpdateMLE(threadmle, buf, mutex); | |
560 // Start the work threads | |
561 workevent->Post(); | |
562 app->MainSleep(100); | |
563 // Reset the work event | |
564 workevent->Reset(); | |
565 inprogress--; | |
566 } | |
567 else | |
568 { | |
569 snprintf(buf, BUF_SIZE, "Control thread error %d.\r\n", result); | |
570 UpdateMLE(threadmle, buf, mutex); | |
571 app->MainSleep(10000); | |
572 } | |
573 } | |
574 // Tell the other threads we are done | |
575 finished = TRUE; | |
576 workevent->Post(); | |
577 // Close the control event | |
578 controlevent->Close(); | |
579 UpdateMLE(threadmle, "Control thread finished.\r\n", mutex); | |
580 } | |
581 | |
469 // Add the menus to the window | 582 // Add the menus to the window |
470 void CreateMenus() { | 583 void CreateMenus() { |
471 // Setup the menu | 584 // Setup the menu |
472 DW::MenuBar *menubar = this->MenuBarNew(); | 585 DW::MenuBar *menubar = this->MenuBarNew(); |
473 | 586 |
539 { | 652 { |
540 DW::Box *lbbox = new DW::Box(DW_VERT, 10); | 653 DW::Box *lbbox = new DW::Box(DW_VERT, 10); |
541 | 654 |
542 notebookbox->PackStart(lbbox, 150, 70, TRUE, TRUE, 0); | 655 notebookbox->PackStart(lbbox, 150, 70, TRUE, TRUE, 0); |
543 | 656 |
544 /* Copy and Paste */ | 657 // Copy and Paste |
545 DW::Box *browsebox = new DW::Box(DW_HORZ, 0); | 658 DW::Box *browsebox = new DW::Box(DW_HORZ, 0); |
546 lbbox->PackStart(browsebox, 0, 0, FALSE, FALSE, 0); | 659 lbbox->PackStart(browsebox, 0, 0, FALSE, FALSE, 0); |
547 | 660 |
548 DW::Entryfield *copypastefield = new DW::Entryfield(); | 661 DW::Entryfield *copypastefield = new DW::Entryfield(); |
549 copypastefield->SetLimit(260); | 662 copypastefield->SetLimit(260); |
553 browsebox->PackStart(copybutton, FALSE, FALSE, 0); | 666 browsebox->PackStart(copybutton, FALSE, FALSE, 0); |
554 | 667 |
555 DW::Button *pastebutton = new DW::Button("Paste"); | 668 DW::Button *pastebutton = new DW::Button("Paste"); |
556 browsebox->PackStart(pastebutton, FALSE, FALSE, 0); | 669 browsebox->PackStart(pastebutton, FALSE, FALSE, 0); |
557 | 670 |
558 /* Archive Name */ | 671 // Archive Name |
559 DW::Text *stext = new DW::Text("File to browse"); | 672 DW::Text *stext = new DW::Text("File to browse"); |
560 stext->SetStyle(DW_DT_VCENTER); | 673 stext->SetStyle(DW_DT_VCENTER); |
561 lbbox->PackStart(stext, 130, 15, TRUE, TRUE, 2); | 674 lbbox->PackStart(stext, 130, 15, TRUE, TRUE, 2); |
562 | 675 |
563 browsebox = new DW::Box(DW_HORZ, 0); | 676 browsebox = new DW::Box(DW_HORZ, 0); |
594 //this->ClickDefault(cancelbutton); | 707 //this->ClickDefault(cancelbutton); |
595 | 708 |
596 DW::Button *colorchoosebutton = new DW::Button("Color Chooser Dialog"); | 709 DW::Button *colorchoosebutton = new DW::Button("Color Chooser Dialog"); |
597 buttonbox->PackStart(colorchoosebutton, 130, 30, TRUE, TRUE, 2); | 710 buttonbox->PackStart(colorchoosebutton, 130, 30, TRUE, TRUE, 2); |
598 | 711 |
599 /* Set some nice fonts and colors */ | 712 // Set some nice fonts and colors |
600 lbbox->SetColor(DW_CLR_DARKCYAN, DW_CLR_PALEGRAY); | 713 lbbox->SetColor(DW_CLR_DARKCYAN, DW_CLR_PALEGRAY); |
601 buttonbox->SetColor(DW_CLR_DARKCYAN, DW_CLR_PALEGRAY); | 714 buttonbox->SetColor(DW_CLR_DARKCYAN, DW_CLR_PALEGRAY); |
602 okbutton->SetColor(DW_CLR_PALEGRAY, DW_CLR_DARKCYAN); | 715 okbutton->SetColor(DW_CLR_PALEGRAY, DW_CLR_DARKCYAN); |
603 #ifdef COLOR_DEBUG | 716 #ifdef COLOR_DEBUG |
604 copypastefield->SetColor(DW_CLR_WHITE, DW_CLR_RED); | 717 copypastefield->SetColor(DW_CLR_WHITE, DW_CLR_RED); |
990 { | 1103 { |
991 this->DrawShapes(FALSE, pixmap); | 1104 this->DrawShapes(FALSE, pixmap); |
992 } | 1105 } |
993 else if(page_num == 1) | 1106 else if(page_num == 1) |
994 { | 1107 { |
995 /* Get the font size for this printer context... */ | 1108 // Get the font size for this printer context... |
996 int fheight, fwidth; | 1109 int fheight, fwidth; |
997 | 1110 |
998 /* If we have a file to display... */ | 1111 // If we have a file to display... |
999 if(current_file) | 1112 if(current_file) |
1000 { | 1113 { |
1001 int nrows; | 1114 int nrows; |
1002 | 1115 |
1003 /* Calculate new dimensions */ | 1116 // Calculate new dimensions |
1004 pixmap->GetTextExtents("(g", NULL, &fheight); | 1117 pixmap->GetTextExtents("(g", NULL, &fheight); |
1005 nrows = (int)(pixmap->GetHeight() / fheight); | 1118 nrows = (int)(pixmap->GetHeight() / fheight); |
1006 | 1119 |
1007 /* Do the actual drawing */ | 1120 // Do the actual drawing |
1008 this->DrawFile(0, 0, nrows, fheight, pixmap); | 1121 this->DrawFile(0, 0, nrows, fheight, pixmap); |
1009 } | 1122 } |
1010 else | 1123 else |
1011 { | 1124 { |
1012 /* We don't have a file so center an error message on the page */ | 1125 // We don't have a file so center an error message on the page |
1013 const char *text = "No file currently selected!"; | 1126 const char *text = "No file currently selected!"; |
1014 int posx, posy; | 1127 int posx, posy; |
1015 | 1128 |
1016 pixmap->GetTextExtents(text, &fwidth, &fheight); | 1129 pixmap->GetTextExtents(text, &fwidth, &fheight); |
1017 | 1130 |
1072 DW::Tree *tree = new DW::Tree(); | 1185 DW::Tree *tree = new DW::Tree(); |
1073 if(tree->GetHWND()) | 1186 if(tree->GetHWND()) |
1074 { | 1187 { |
1075 notebookbox->PackStart(tree, 500, 200, TRUE, TRUE, 1); | 1188 notebookbox->PackStart(tree, 500, 200, TRUE, TRUE, 1); |
1076 | 1189 |
1077 /* and a status area to see whats going on */ | 1190 // and a status area to see whats going on |
1078 DW::StatusText *tree_status = new DW::StatusText(); | 1191 DW::StatusText *tree_status = new DW::StatusText(); |
1079 notebookbox->PackStart(tree_status, 100, DW_SIZE_AUTO, TRUE, FALSE, 1); | 1192 notebookbox->PackStart(tree_status, 100, DW_SIZE_AUTO, TRUE, FALSE, 1); |
1080 | 1193 |
1081 // set up our signal trappers... | 1194 // set up our signal trappers... |
1082 tree->ConnectItemContext([this, tree_status](char *text, int x, int y, void *data) -> int | 1195 tree->ConnectItemContext([this, tree_status](char *text, int x, int y, void *data) -> int |
1488 } | 1601 } |
1489 free(text); | 1602 free(text); |
1490 // now insert a couple of items | 1603 // now insert a couple of items |
1491 combobox2->Insert("inserted item 2", 2); | 1604 combobox2->Insert("inserted item 2", 2); |
1492 combobox2->Insert("inserted item 5", 5); | 1605 combobox2->Insert("inserted item 5", 5); |
1493 /* make a spinbutton */ | 1606 // make a spinbutton |
1494 DW::SpinButton *spinbutton = new DW::SpinButton(); | 1607 DW::SpinButton *spinbutton = new DW::SpinButton(); |
1495 combox->PackStart(spinbutton, TRUE, FALSE, 0); | 1608 combox->PackStart(spinbutton, TRUE, FALSE, 0); |
1496 spinbutton->SetLimits(100, 1); | 1609 spinbutton->SetLimits(100, 1); |
1497 spinbutton->SetPos(30); | 1610 spinbutton->SetPos(30); |
1498 | 1611 |
1499 spinbutton->ConnectValueChanged([this](int value) -> int | 1612 spinbutton->ConnectValueChanged([this](int value) -> int |
1500 { | 1613 { |
1501 this->app->MessageBox("DWTest", DW_MB_OK, "New value from spinbutton: %d\n", value); | 1614 this->app->MessageBox("DWTest", DW_MB_OK, "New value from spinbutton: %d\n", value); |
1502 return TRUE; | 1615 return TRUE; |
1503 }); | 1616 }); |
1504 /// make a slider | 1617 // make a slider |
1505 DW::Slider *slider = new DW::Slider(FALSE, 11, 0); | 1618 DW::Slider *slider = new DW::Slider(FALSE, 11, 0); |
1506 combox->PackStart(slider, TRUE, FALSE, 0); | 1619 combox->PackStart(slider, TRUE, FALSE, 0); |
1507 | 1620 |
1508 // make a percent | 1621 // make a percent |
1509 DW::Percent *percent = new DW::Percent(); | 1622 DW::Percent *percent = new DW::Percent(); |
1646 // Page 7 - ScrollBox | 1759 // Page 7 - ScrollBox |
1647 void CreateScrollBox(DW::Box *notebookbox) | 1760 void CreateScrollBox(DW::Box *notebookbox) |
1648 { | 1761 { |
1649 char buf[101] = {0}; | 1762 char buf[101] = {0}; |
1650 | 1763 |
1651 /* create a box to pack into the notebook page */ | 1764 // create a box to pack into the notebook page |
1652 DW::ScrollBox *scrollbox = new DW::ScrollBox(DW_VERT, 0); | 1765 DW::ScrollBox *scrollbox = new DW::ScrollBox(DW_VERT, 0); |
1653 notebookbox->PackStart(scrollbox, 0, 0, TRUE, TRUE, 1); | 1766 notebookbox->PackStart(scrollbox, 0, 0, TRUE, TRUE, 1); |
1654 | 1767 |
1655 DW::Button *adjbutton = new DW::Button("Show Adjustments", 0); | 1768 DW::Button *adjbutton = new DW::Button("Show Adjustments", 0); |
1656 scrollbox->PackStart(adjbutton, FALSE, FALSE, 0); | 1769 scrollbox->PackStart(adjbutton, FALSE, FALSE, 0); |
1672 snprintf(buf, 100, "Entry %d", i); | 1785 snprintf(buf, 100, "Entry %d", i); |
1673 DW::Entryfield *entry = new DW::Entryfield(buf , i); | 1786 DW::Entryfield *entry = new DW::Entryfield(buf , i); |
1674 tmpbox->PackStart(entry, 0, DW_SIZE_AUTO, TRUE, FALSE, 0); | 1787 tmpbox->PackStart(entry, 0, DW_SIZE_AUTO, TRUE, FALSE, 0); |
1675 } | 1788 } |
1676 } | 1789 } |
1790 | |
1791 // Page 8 - Thread and Event | |
1792 void CreateThreadEvent(DW::Box *notebookbox) | |
1793 { | |
1794 // create a box to pack into the notebook page | |
1795 DW::Box *tmpbox = new DW::Box(DW_VERT, 0); | |
1796 notebookbox->PackStart(tmpbox, 0, 0, TRUE, TRUE, 1); | |
1797 | |
1798 DW::Button *startbutton = new DW::Button("Start Threads"); | |
1799 tmpbox->PackStart(startbutton, FALSE, FALSE, 0); | |
1800 | |
1801 // Create the base threading components | |
1802 DW::MLE *threadmle = new DW::MLE(); | |
1803 tmpbox->PackStart(threadmle, 1, 1, TRUE, TRUE, 0); | |
1804 DW::Mutex *mutex = new DW::Mutex(); | |
1805 DW::Event *workevent = new DW::Event(); | |
1806 | |
1807 startbutton->ConnectClicked([this, mutex, workevent, threadmle, startbutton]() -> int | |
1808 { | |
1809 startbutton->Disable(); | |
1810 mutex->Lock(); | |
1811 DW::Event *controlevent = new DW::Event(); | |
1812 workevent->Reset(); | |
1813 finished = FALSE; | |
1814 ready = 0; | |
1815 UpdateMLE(threadmle, "Starting thread 1\r\n", DW_NULL); | |
1816 new DW::Thread([this, startbutton, mutex, controlevent, workevent, threadmle](DW::Thread *thread) -> void { | |
1817 this->RunThread(1, mutex, controlevent, workevent, threadmle); | |
1818 }); | |
1819 UpdateMLE(threadmle, "Starting thread 2\r\n", DW_NULL); | |
1820 new DW::Thread([this, startbutton, mutex, controlevent, workevent, threadmle](DW::Thread *thread) -> void { | |
1821 this->RunThread(2, mutex, controlevent, workevent, threadmle); | |
1822 }); | |
1823 UpdateMLE(threadmle, "Starting thread 3\r\n", DW_NULL); | |
1824 new DW::Thread([this, startbutton, mutex, controlevent, workevent, threadmle](DW::Thread *thread) -> void { | |
1825 this->RunThread(3, mutex, controlevent, workevent, threadmle); | |
1826 }); | |
1827 UpdateMLE(threadmle, "Starting thread 4\r\n", DW_NULL); | |
1828 new DW::Thread([this, startbutton, mutex, controlevent, workevent, threadmle](DW::Thread *thread) -> void { | |
1829 this->RunThread(4, mutex, controlevent, workevent, threadmle); | |
1830 }); | |
1831 UpdateMLE(threadmle, "Starting control thread\r\n", DW_NULL); | |
1832 new DW::Thread([this, startbutton, mutex, controlevent, workevent, threadmle](DW::Thread *thread) -> void { | |
1833 this->ControlThread(mutex, controlevent, workevent, threadmle); | |
1834 startbutton->Enable(); | |
1835 }); | |
1836 mutex->Unlock(); | |
1837 return FALSE; | |
1838 }); | |
1839 } | |
1677 public: | 1840 public: |
1678 // Constructor creates the application | 1841 // Constructor creates the application |
1679 DWTest(const char *title): DW::Window(title) { | 1842 DWTest(const char *title): DW::Window(title) { |
1680 // Get our application singleton | 1843 // Get our application singleton |
1681 app = DW::App::Init(); | 1844 app = DW::App::Init(); |
1690 /* First try the current directory */ | 1853 /* First try the current directory */ |
1691 foldericon = app->LoadIcon(foldericonpath); | 1854 foldericon = app->LoadIcon(foldericonpath); |
1692 fileicon = app->LoadIcon(fileiconpath); | 1855 fileicon = app->LoadIcon(fileiconpath); |
1693 | 1856 |
1694 #ifdef PLATFORMFOLDER | 1857 #ifdef PLATFORMFOLDER |
1695 /* In case we are running from the build directory... | 1858 // In case we are running from the build directory... |
1696 * also check the appropriate platform subfolder | 1859 // also check the appropriate platform subfolder |
1697 */ | |
1698 if(!foldericon) | 1860 if(!foldericon) |
1699 { | 1861 { |
1700 strncpy(foldericonpath, PLATFORMFOLDER "folder", 1024); | 1862 strncpy(foldericonpath, PLATFORMFOLDER "folder", 1024); |
1701 foldericon = app->LoadIcon(foldericonpath); | 1863 foldericon = app->LoadIcon(foldericonpath); |
1702 } | 1864 } |
1705 strncpy(fileiconpath, PLATFORMFOLDER "file", 1024); | 1867 strncpy(fileiconpath, PLATFORMFOLDER "file", 1024); |
1706 fileicon = app->LoadIcon(fileiconpath); | 1868 fileicon = app->LoadIcon(fileiconpath); |
1707 } | 1869 } |
1708 #endif | 1870 #endif |
1709 | 1871 |
1710 /* Finally try from the platform application directory */ | 1872 // Finally try from the platform application directory |
1711 if(!foldericon && !fileicon) | 1873 if(!foldericon && !fileicon) |
1712 { | 1874 { |
1713 char *appdir = app->GetDir(); | 1875 char *appdir = app->GetDir(); |
1714 char pathbuff[1025] = {0}; | 1876 char pathbuff[1025] = {0}; |
1715 int pos = (int)strlen(appdir); | 1877 int pos = (int)strlen(appdir); |
1782 CreateScrollBox(notebookbox); | 1944 CreateScrollBox(notebookbox); |
1783 notebookpage = notebook->PageNew(); | 1945 notebookpage = notebook->PageNew(); |
1784 notebook->Pack(notebookpage, notebookbox); | 1946 notebook->Pack(notebookpage, notebookbox); |
1785 notebook->PageSetText(notebookpage, "scrollbox"); | 1947 notebook->PageSetText(notebookpage, "scrollbox"); |
1786 | 1948 |
1949 // Create Notebook Page 8 - Thread and Event | |
1950 notebookbox = new DW::Box(DW_VERT, 5); | |
1951 CreateThreadEvent(notebookbox); | |
1952 notebookpage = notebook->PageNew(); | |
1953 notebook->Pack(notebookpage, notebookbox); | |
1954 notebook->PageSetText(notebookpage, "thread/event"); | |
1955 | |
1787 // Finalize the window | 1956 // Finalize the window |
1788 this->SetSize(640, 550); | 1957 this->SetSize(640, 550); |
1789 | 1958 |
1790 timer = new DW::Timer(2000, [this]() -> int | 1959 timer = new DW::Timer(2000, [this]() -> int |
1791 { | 1960 { |
1819 char **lp; | 1988 char **lp; |
1820 char *current_file = NULL; | 1989 char *current_file = NULL; |
1821 | 1990 |
1822 // Page 4 | 1991 // Page 4 |
1823 int mle_point=-1; | 1992 int mle_point=-1; |
1993 | |
1994 // Page 8 | |
1995 int finished = FALSE, ready = 0; | |
1824 | 1996 |
1825 // Miscellaneous | 1997 // Miscellaneous |
1826 int menu_enabled = TRUE; | 1998 int menu_enabled = TRUE; |
1827 HICN fileicon, foldericon; | 1999 HICN fileicon, foldericon; |
1828 char fileiconpath[1025] = "file"; | 2000 char fileiconpath[1025] = "file"; |