במסגרת החיפושים אחרי סביבת פיתוח טובה להדגמות על Raspberry Pi, עליתי על טריק ישן לשדרוג של פלט תוכניות ה-Console הפשוטות. היכונו לטיסה אל העבר הרחוק!
מרגע שהתחלתי לשחק עם מחשבי לוח-יחיד הבנתי שבתחום זה, כדי להראות לכם דברים מעניינים שבאמת יש סיכוי שתנסו בעצמכם, אזדקק לסביבת פיתוח שתמלא מספר תנאים, ביניהם:
- התקנה פשוטה מאד
- גישה קלה לפלט גרפי
- שפת תכנות קלה ו/או מוכרת
- לא מכבידה על מחשבים קטנים
סביבת הפיתוח FPC/Lazarus, שכבר כתבתי בה כמה תוכניות שימושיות, לא ממש עומדת בתנאים האלה. ה-IDE של Basic256, שהיא כמעט מושלמת לצורך העניין בפלטפורמות אחרות, סובלת ממגבלה אחת קטנה ב-Raspberry Pi: היא קורסת בכל פעם שמופיע ערך עם נקודה עשרונית. לתוכנת Geany הידידותית, שבה אפשר לכתוב (בין השאר) קוד ב-C, לא הצלחתי למצוא בינתיים ספריות גרפיות פשוטות. אלא שבמהלך החיפושים מצאתי משהו אחר מעניין. הסתכלו על פלט תוכנית Hello world:
ועל התוכן של חלון LXTerminal (ברירת המחדל לחלון "שורת פקודה" במערכת ההפעלה Raspbian):
חלון הפלט הוא מאותו סוג בדיוק, אך בשפת C אין שום דרך לקבוע את הצבע של הטקסט – היא פשוט שולחת אותו ומה שיהיה יהיה. אז איך מערכת ההפעלה עושה את זה?
כדי לגלות את התשובה, נחזור לאחור בזמן אל שנות השבעים המאוחרות. באותם ימים, מחשב ראוי לשמו עדיין היה מפלצת ענקית ויקרה מאד, כך שברוב המוסדות וחברות ה"היי-טק" היה מחשב יחיד מרכזי (או בודדים), והמשתמשים השונים התחברו אליו בעזרת טרמינלים – מערכות שכללו מסך, מקלדת ולא הרבה יותר מזה. VT100, למשל, היה טרמינל מפורסם ונפוץ במיוחד. הטרמינל שידר למחשב מה שהמשתמש הקליד, המחשב עיבד, ושידר בחזרה לטרמינל מה שצריך להציג על המסך. השידור היה סריאלי ואיטי, בקוד ANSI טקסטואלי בסיסי.
צורת התקשורת מהמחשב לטרמינל יצרה בעיה מסוימת. אם הטרמינל פשוט "ישפוך" על המסך כל תו חדש שמגיע, בצורה עיוורת משמאל לימין ומלמעלה למטה, הטקסט יתגלגל כמו דף במדפסת שאין לו סוף. נסו לדמיין כמה קשה, למשל, לערוך מסמך בשיטה כזו! זה סיוט גם למשתמש וגם למחשב המרכזי, שיצטרך לשלוח מחדש את כל המידע אפילו אם רק תו אחד השתנה אי-שם במעלה המסך.
כדי לפתור את הבעיה, הומצא תקן מסודר פחות או יותר לשליחה של קודי פעולה מוסכמים ("escape codes"), שמגיעים בצורת תווי טקסט אך אינם טקסט רגיל ואינם מוצגים על המסך. קודים כאלה יכולים להגיד לטרמינל, למשל, לעבור למיקום מסוים על המסך, לנקות את המסך כולו, ובטרמינלים שתומכים בצבע – גם לשנות צבע ורקע של תווים כדי להעשיר את התצוגה. הנה פירוט מקיף למדי של ה-ANSI escape codes.
הטרמינלים הלכו ונעלמו מהעולם (אם כי בטח עוד יש אי-שם כמה פעילים), אך תקן ה-escape codes לא נעלם איתם, מכיוון שכל מערכת מחשב חדשה דאגה לתאימות-לאחור וסיפקה פונקציונליות דמויית טרמינל כזו או אחרת. וכאן אנחנו חוזרים לנקודת המוצא – חלון ה-LXTerminal. כפי ששמו מרמז, זהו אינו סתם חלון טקסט אלא אמולציה של הטרמינל הפיזי הישן והטוב – ולכן, אנחנו יכולים לשלוח לו את אותם escape codes!
ה-Escape code הטיפוסי מתחיל בתו ASCII שמספרו 27 (בבסיס 16 זה 0x1B), ואחריו סוגר מרובע שמאלי ("]"). לאחר מכן מגיעים תווים נוספים שמתארים את הפונקציה הספציפית של הקוד, וכן פרמטרים. לדוגמה, הקוד שמציב את הסמן במיקום 8,12 על המסך יורכב בצורה כזו:
0x1B + "[12;8f"
שימו לב, זה לא יופיע בדיוק ככה בתוכנה – שמתי את הערך של התו הראשון מחוץ למחרוזת, כי אין לו ייצוג גרפי מקובל בקוד ASCII (וזו מן הסתם אחת הסיבות שנבחר לתפקיד). כמו כן, סדר הקואורדינטות בפקודה הוא קודם Y ואחר כך X. בשפת C אנחנו יכולים לכתוב פונקציה שמעבירה את הסמן למיקום x,y במסך הטרמינל בעזרת פקודת printf, כך:
void gotoXY(const int x, const int y) { printf("%c[%d;%df", 0x1B, y, x); }
אפשר לכתוב פונקציות דומות לשינוי צבעים ולכל שאר האפשרויות של ה-escape codes, ולהגיע מהר מאד לתוצאות כמו זו שבתחילת הפוסט, ומשוכללות עוד יותר.
שיטה זו רחוקה מרחק רב מגרפיקה אמתית, והיא מגושמת למדי. אף על פי כן, אחרי שמבינים את הפרנציפ, אפשר לבצע איתה הרבה דברים נחמדים ולתת קצת חיים לפלט הטקסטואלי היבש והמוכר – בלי ספריות ענקיות להתקנה, ועם דרישות משאבים אפסיות.
למתעניינים, לוח הצבעים בו השתמשתי למעלה (256 ערכים) מפורט כאן. חלון ה-Console ב-Windows אינו מקבל escape codes – כדי לשלוט בו תצטרכו לעבוד בשיטה אחרת.