2 คะแนน โดย GN⁺ 2024-04-02 | 1 ความคิดเห็น | แชร์ทาง WhatsApp
  • Libmui เป็นไลบรารี UI ที่จำลอง API “Toolbox” ของ Macintosh Classic ขึ้นมาจำนวนมาก แม้จะยังไม่ครบถ้วนทั้งหมด แต่มีฟีเจอร์ที่จำเป็นสำหรับ MII Apple //e emulator และแอปพลิเคชันง่าย ๆ
  • เริ่มต้นจากการเป็นไลบรารี UI สำหรับ MII โดยมีเป้าหมายเป็น UI ที่จัดวางด้วยมือ แทนการพึ่งพา dependency จำนวนมากหรือเมนูสไตล์เกมแบบ “Arrow Keys + Return + Escape”
  • การเรนเดอร์ทำโดยวาดลงบน บัฟเฟอร์ ARGB แล้วคัดลอกไปยัง OpenGL texture หรือ X11/XCB shared pixmap พร้อมติดตาม invalid region เพื่อวาดซ้ำเฉพาะส่วนที่จำเป็น
  • API ทำงานแบบ อะซิงโครนัสและอิง callback ต่างจาก Macintosh Toolbox เดิม โดยเมื่อสถานะเปลี่ยน UI จะวาดใหม่เมื่อจำเป็น และส่งอีเวนต์ผ่าน callback แทนการ polling
  • มีฟังก์ชัน Window, Menu, Control, List, Alert และ Standard File แต่ยังไม่รองรับ zooming, resizing, Save dialog, dark mode, theme, Wayland, GTK/QT/SDL หรือ binding สำหรับ Rust/Go/Python

สิ่งที่ Libmui สร้างขึ้น

  • Libmui เป็นไลบรารีที่จำลอง API “Toolbox” ของ Macintosh Classic มาจำนวนมาก
  • แม้จะยังไม่ใช่การทำซ้ำแบบสมบูรณ์ แต่ครอบคลุมส่วนที่จำเป็นสำหรับแอปง่าย ๆ บางตัวและ MII Apple //e emulator
  • จุดเริ่มต้นมาจากความต้องการไลบรารี UI สำหรับ MII ที่ไม่มี dependency มากเกินไป และไม่โน้มเอียงไปทาง UI ที่ควบคุมเมนูแบบเกม
  • เคยลองใช้ Nuklear immediate mode UI มาก่อน แต่ไม่ชอบหน้าตา มีข้อจำกัดในการปรับแต่ง และมองว่า layout engine จัดวางองค์ประกอบไม่ตรงตำแหน่งที่ต้องการ
  • อีกเหตุผลหนึ่งคือประสบการณ์ที่พบว่า immediate mode UI เก็บสถานะภายในด้วย hash ซึ่ง hash collision สามารถก่อปัญหาในการดีบักได้จริง
  • เป้าหมายคือ UI ที่ใกล้เคียงกับ UI ที่ขัดเกลาด้วยมือโดยตรง มากกว่า UI ที่ปล่อยให้ layout engine ตัดสินใจเอง

โมเดลการเรนเดอร์และการทำงาน

  • Libmui วาด UI ลงบน “screen” ที่เป็น บัฟเฟอร์ ARGB
    • ใน MII บัฟเฟอร์นี้จะถูก overlay เป็น OpenGL texture
    • เดโม playground ในโฟลเดอร์ example จะคัดลอกไปยังหน้าต่าง X11 ด้วย XCB shared pixmap และทำงานผ่าน remote X11 ได้
  • เหมือนระบบปฏิบัติการยุคเก่า คือจะติดตาม invalid region แล้ววาดใหม่เฉพาะส่วนที่จำเป็น
    • จึงไม่ต้องวาดทั้งหน้าจอใหม่ทุกครั้ง ทำให้ overdraw ต่ำมาก
    • หากต้องวาดลง framebuffer 16 บิต เป็นต้น ก็ต้องแปลงจากเอาต์พุต ARGB โดยตรง
    • แต่เพราะแปลงเฉพาะ dirty region ภาระจึงไม่สูงนัก
  • แม้จะสามารถเปลี่ยนการเรนเดอร์ให้เป็นแบบเวกเตอร์ เช่น vertex buffer ได้ แต่ผู้พัฒนามองว่าวิธีปัจจุบันเร็วพอแล้ว และไม่จำเป็นต้องย้อนกลับไปใช้แนวทางวาดใหม่ทั้งหน้าจอแบบ immediate mode UI

จุดที่ต่างจาก Macintosh Toolbox ดั้งเดิม

  • รูปลักษณ์เริ่มต้นจาก MacOS 8/9 แต่ให้ความรู้สึกเหมือนตัดองค์ประกอบโทน grayscale ออก และเลือกแนวทางของ System 7 เพราะมองว่าหน้าตาแบบ flat ดูเก่าได้ดีกว่า
  • popup menu ใกล้เคียงกับ OS8 มากกว่า ส่วน scrollbar ใกล้ GS/OS มากกว่า
  • ความต่างสำคัญของ API คือทำงานแบบ อะซิงโครนัสเต็มรูปแบบ
    • จึงไม่สามารถวาดลง window หรือ GrafPort ได้ทุกเมื่อด้วย spinloop แบบเดิม
    • เมื่อสถานะ UI เปลี่ยน ตัว UI จะวาดใหม่เองเมื่อจำเป็น
  • การจัดการอีเวนต์เป็นแบบ อิง callback
    • ไม่ต้อง polling เพื่อตรวจว่าเกิดอะไรขึ้นกับ UI
    • เมื่อมีการคลิกเมนูไอเท็มหรือกดคีย์ลัด จะมีการเรียก action callback
  • โครงสร้างเชิงแนวคิดเรียบง่ายกว่าต้นฉบับ
    • ทุกอย่างคือ mui_window หรือ mui_control
    • windows, menubars, menus คือ mui_window
    • ชื่อเมนู, รายการเมนู, องค์ประกอบทั้งหมดในหน้าต่าง และเส้นคั่น คือ mui_control

ตัวจัดการและคอนโทรลที่มีให้

  • Window Manager

    • รองรับการสร้างหน้าต่างและการวาดภายในหน้าต่าง
    • รองรับสูงสุด 15 layer, clipping, การทำงานแบบ BringToFront และการลากหน้าต่าง
    • ระบบพิกัดถูกจำกัดไว้คล้ายของเดิม คือมีแค่ screen coordinates และ window content coordinates
    • จัดการรายการ invalid rectangle เพื่อไม่ต้องวาดทั้งหน้าต่างใหม่ทุกครั้ง
    • zooming และ resizing ยังเป็น TODO
    • ตั้งใจไม่รองรับ transparent windows
      • เพราะใช้การวาดหน้าต่างจากบนลงล่างเพื่อเพิ่มประสิทธิภาพของ clipping
      • หากต้องจัดการความโปร่งใสจะต้องวาดจากล่างขึ้นบน และจะต้องวาดเนื้อหาซ้ำมากขึ้น
      • อย่างไรก็ตาม ยังสามารถทำ alpha blend ทั้งหน้าจอ UI ไปยังตำแหน่งที่ต้องการได้
  • Menu Manager

    • รองรับ menubar, menus, checkmarks และ keyboard shortcuts
    • ออกแบบให้ดูเหมือน System 7/8 หรือ GS/OS
    • มี hierarchical menus แต่ยังไม่เหมือนต้นฉบับทั้งหมดและยังควรปรับปรุง
    • การแสดงผลและการเลื่อนของ popup ขนาดใหญ่มากยังเป็น TODO
    • รองรับ sticky menus ไปบางส่วนแล้ว แต่ยังทำงานไม่ถูกต้องจึงปิดไว้
  • Control Manager

    • รองรับ buttons, checkboxes, radio buttons, vertical scrollbars และ textboxes แบบตัดบรรทัด
    • Edit Field ยังอยู่ระหว่างพัฒนา และ Slider ยังไม่มี
    • ต้นแบบ text edit control ใช้ได้ดีกับการป้อนข้อความบรรทัดเดียว แต่ยังไม่เหมาะกับกล่องข้อความหลายบรรทัด
  • List Manager

    • ตอนนี้ยังถูก hardcode ไว้ใกล้เคียงกับการแสดงชื่อไฟล์
    • รองรับ arrow keys, page up/down และ scroll wheel
    • เหมือน MacOS ดั้งเดิมตรงที่ค้นหารายการที่ต้องการได้ด้วย typeahead
    • ความสามารถในการใช้ font compression หรือ ellipsis abbreviation เมื่อข้อความยาวเกินไปยังเป็น TODO
  • Alerts และ Standard File

    • Alert มี dialog ทั่วไปแบบ Cancel + OK
    • alert แบบอื่น ๆ ยังเป็น TODO
    • Standard File ให้ open file dialog แบบคลาสสิก
      • ฟังก์ชันนี้เป็นหนึ่งในเป้าหมายหลักช่วงแรกของไลบรารี
      • Save dialog ยังเป็น TODO
      • มี popup เพิ่มเติมสำหรับแสดงไดเรกทอรีที่ใช้ล่าสุด
      • รองรับ arrow keys, page up/down และการค้นหาไฟล์แบบ typeahead
  • Resource Manager

    • ไม่มี Resource Manager
    • มองว่าจำเป็นต้องมีเครื่องมืออย่าง ResEdit จึงยังอยู่นอกขอบเขตปัจจุบัน
    • มีไอเดียเรื่องฟอร์แมต MessagePack สำหรับ resource แต่เก็บไว้เป็นงานในอนาคต

Dependency และการบิลด์

  • dependency ภายนอกมีเพียง libpixman
    • libpixman เป็นไลบรารีสำหรับจัดการพิกเซล และมีฟังก์ชัน region ที่มีประโยชน์ต่อ clipping
    • แม้จะไม่ดีเท่า region ของ QuickDraw แต่ก็มองว่าเพียงพอ
  • ยังมีองค์ประกอบที่รวมมาในซอร์สด้วย
    • libcg: renderer แบบ antialiased ขนาดเล็กคล้าย cairo ประกอบด้วย 2 ไฟล์
    • stb_truetype.h: ใช้สำหรับโหลดฟอนต์ TrueType
    • stb_ttc.h: ส่วนขยายของ stb_truetype.h สำหรับสร้าง dictionary ของฟอนต์/กริฟ, hash table และ font texture
    • โค้ด 2D geometry มีอายุมากกว่า 25 ปี และรวมอยู่ใน libc3 ด้วย
  • การบิลด์ใช้ Makefile แบบเรียบง่าย โดยรัน make จากไดเรกทอรีราก
  • การบิลด์ tests, demos, samples ต้องใช้ xcb, xcb-shm, xcb-randr, xkbcommon-x11
  • หากใช้ Nvidia binary driver จะต้องเพิ่ม Option "AllowSHMPixmaps" "1" ใน Device ของ /etc/X11/xorg.conf เพื่อให้ mui_shell ทำงานได้

วิธีใช้งานและเวิร์กโฟลว์การพัฒนา

  • แนะนำให้เริ่มจากแก้ไข mui_shell.c และ mui_widgets_demo.c
  • ui_mui_shell จะโหลด mui_widgets_demo.so เป็น plugin และ reload อัตโนมัติเมื่อพบการเปลี่ยนแปลง
  • เมื่อแก้ mui_widgets_demo.c แล้ว ระบบจะ reload และรันใหม่ ทำให้สร้าง dialog ใหม่ได้อย่างรวดเร็ว
  • หากรัน make watch ในไดเรกทอรี libmui จะมีการบิลด์ไลบรารีและ mui_shell ใหม่โดยอัตโนมัติเมื่อมีการเปลี่ยนแปลง
  • เมื่อนำไปใช้ร่วมกับ auto save ของตัวแก้ไข ก็จะได้เวิร์กโฟลว์แบบแก้ไขไปพร้อมกับบิลด์และรันต่อเนื่อง

สิ่งที่ไม่มีให้โดยชัดเจน

  • ไม่มี dark mode
  • ไม่รองรับ theme
  • ไม่มี transparent windows และ cube effect
  • sticky menus ยังไม่เปิดใช้งานในตอนนี้
  • ไม่ใช้ cmake, meson, ninja, autotools
  • ไม่มี language binding สำหรับ Rust, Go, Python
  • ไม่ใช้เฟรมเวิร์กอย่าง GTK, QT
  • ไม่ใช้ SDL
  • ไม่รองรับ Wayland

1 ความคิดเห็น

 
GN⁺ 2024-04-02
ความคิดเห็นจาก Hacker News
  • ที่เกี่ยวข้องกัน มีฟอนต์ TrueType แบบ public domain ที่ทำซ้ำ ฟอนต์ระบบ Chicago ต้นฉบับได้ค่อนข้างดี: https://fontlibrary.org/en/font/chicagoflf

    • เคยคิดว่าจะใช้ Chicago เป็นฟอนต์เริ่มต้น แต่ภาพจำของมัน ตายตัว เกินไป
      Charcoal ที่ใช้หลัง System 8.x เป็นต้นมานั้นเป็นที่รู้จักน้อยกว่ามาก และโดยส่วนตัวผมมองว่าเป็นการปรับปรุงที่ดีขึ้นพอสมควร
      ถึงอย่างนั้น การเปลี่ยนเป็น Chicago ในไลบรารีจริง ๆ แล้วค่อนข้างง่าย นอกจากตัวทำซ้ำที่พูดถึงแล้ว ยังมีเวอร์ชัน TTF แบบ “plain” ของ Chicago ดั้งเดิมลอยอยู่ที่ไหนสักแห่งด้วย
    • เกี่ยวกับเรื่องที่ว่ามีตัวทำซ้ำที่ยอดเยี่ยมของ “ฟอนต์ระบบ Chicago ต้นฉบับ” จริง ๆ แล้วคุณยังสามารถได้ TrueType Chicago ของแท้ ที่ออกแบบโดย Bigelow & Holmes ผ่านการดาวน์โหลด System 7.6.1 ได้ด้วย
      จากนั้นใช้เครื่องมือบรรทัดคำสั่งของ FontForge แปลง TTF เป็น OTF ได้: https://www.macintoshrepository.org/1682-mac-os-7-6-x
      fontforge -script -c 'Open($1); Generate($2);' input_font.ttf output_font.otf
    • macOS รุ่นใหม่ ๆ ยังมีฟอนต์ภาษาไทยชื่อ Silom รวมอยู่ด้วย ซึ่งใช้ Chicago สำหรับ glyph ภาษาละติน
  • เจ๋งมาก Michel ทำสิ่งนี้ไว้สำหรับ Apple II emulator ของเขา ส่วนผมเอามาใช้แบบกึ่งเล่น ๆ เพื่อแทนที่ฟรอนต์เอนด์ของ Archimedes emulator
    ยังอยู่ช่วงต้น ๆ แต่ถ้าผมเข้าใจ API ได้ ก็แปลว่า API มันต้องดีแน่ ๆ :)

    • แม้แต่การใช้กับ Apple II emulator ก็ยังให้ความรู้สึกเหมือนทำเล่น ๆ อยู่บ้าง เพราะผู้ใช้ Apple II ในยุคนั้นอยากให้ Apple สานต่อไลน์ Apple II ต่อไป แต่พอ Apple ไปโฟกัสที่ Mac ก็เหมือนถูกทิ้งไป จึง ไม่ค่อยปลื้ม Mac กัน
      ถึงอย่างนั้น ผมก็ยังรักอินเทอร์เฟซ Mac แบบคลาสสิกอยู่ดี
  • ผมชอบ 2D graphics rasterizer แบบ dual-header ที่โปรเจกต์นี้ใช้: https://github.com/xboot/libcg
    น่าทึ่งเสมอที่สามารถสร้างซอฟต์แวร์ทรงพลังได้ด้วย dependency ขั้นต่ำแบบนี้
    การสร้าง UI library ที่เรียบร้อยและทรงพลังพอจะเป็นทางเลือกแทน Electron มันยากขนาดนั้นเลยหรือ?

    • ไม่หรอก แต่ถ้าอย่างนั้นจะเอา หน่วยความจำกับประสิทธิภาพ ที่เหลือเฟือไปใช้กับอะไรล่ะ?
  • ยอดเยี่ยมมาก เป็น MIT license และเขียนด้วย C! ถ้าเพิ่ม shim สำหรับ AppKit API ก็อาจพอแข่งกับ GNUstep ได้ด้วยซ้ำ

    • หรือไม่ก็แค่ใส่ ธีม GNUstep ก็พอ มันรองรับธีมแบบนี้อยู่แล้ว
  • ดูดีเลย อยากให้เปลี่ยน UI ทั้งหมดของ macOS ของผมให้เป็นแบบนั้นได้จัง

    • ถ้าเปิด โหมดคอนทราสต์สูง ในการตั้งค่าการช่วยการเข้าถึง ก็จะออกมาเกือบคล้ายกัน
  • เป็นโปรเจกต์ที่ดี ผมเคยชอบ Classic Mac UI แบบเก่ามาก
    ตัวอย่างทั้งหมดดูยอดเยี่ยม และพอดูโค้ดเดโมวิดเจ็ตแล้วก็ดูใช้งานง่ายด้วย

  • เจ๋งจริง ๆ สงสัยว่าต้องใช้ความพยายามแค่ไหนในการอ่านไฟล์ resource (.rsrc) ของ resource fork แล้วสร้าง UI จากมัน
    ถ้าทำได้ก็น่าจะใช้ ResEdit ได้ :-)