Menulis Ulang Aplikasi Ghostty GTK
(mitchellh.com)- Tim Ghostty menulis ulang aplikasi GTK sepenuhnya dan secara aktif memanfaatkan sistem tipe GObject
- Dalam proses ini, integrasi dengan bahasa Zig dan verifikasi isu memori melalui Valgrind memainkan peran penting
- Dengan mengadopsi sistem GObject, manajemen memori dan implementasi widget kustom menjadi lebih sederhana dibanding sebelumnya
- Dengan memanfaatkan Valgrind, mereka merasakan bahwa keamanan memori Ghostty meningkat secara signifikan
- Ghostty GTK yang baru kini menjadi default untuk build dari source dan dijadwalkan akan masuk ke rilis 1.2
Pendahuluan
- Ghostty adalah emulator terminal lintas platform yang mendukung macOS, Linux, FreeBSD
- Ghostty memiliki keunggulan dengan menggunakan framework GUI native di tiap platform
- macOS: aplikasi besar berbasis Swift dan Xcode
- Linux dan BSD: aplikasi berbasis GTK, terintegrasi langsung dengan X11/Wayland dan lainnya
- Core bersama ditulis dalam Zig dan menyediakan API yang kompatibel dengan C ABI
- Alasan menulis ulang aplikasi GTK dalam struktur sebelumnya dapat dilihat pada PR asli
- Tulisan ini berfokus pada integrasi dengan sistem tipe GObject serta isu memori yang diverifikasi dengan Valgrind
Sistem tipe GObject dan Zig
- Saat menggunakan GTK, strukturnya pada dasarnya mengharuskan untuk berinteraksi dengan sistem tipe GObject
- Di masa lalu, mereka menghindari sistem GObject dan mencoba menyelaraskan sendiri siklus hidup objek Zig tanpa reference counting dan objek GObject, tetapi berulang kali muncul masalah pelepasan memori yang tidak berjalan dengan benar
- Contoh: memori di sisi Zig sudah dibebaskan, tetapi memori di sisi GTK masih hidup, atau sebaliknya, dan situasi ini terus berulang
- Pendekatan seperti ini tidak hanya bermasalah dari sisi correctness, tetapi juga menyulitkan penggunaan fitur khas GTK (event signal, property binding, action)
- Contoh konkretnya, saat me-reload struct konfigurasi (
config), semua elemen GUI yang terhubung harus diperbarui secara konsisten, tetapi proses ini rumit dan sering menimbulkan kesalahan- Sekarang, ini dikelola sebagai
GhosttyConfigGObject dengan reference counting yang membungkus structConfigZig, sehingga perubahan dapat menyebar secara alami ke seluruh aplikasi melalui notifikasi perubahan properti
- Sekarang, ini dikelola sebagai
- Pembuatan widget GObject kustom juga menjadi lebih mudah, sehingga teknologi UI GTK modern seperti Blueprint dapat digunakan
- Baru-baru ini, dengan mengadopsi Blueprint, fitur baru seperti tab titlebar GTK dan bingkai lonceng beranimasi menjadi lebih mudah ditambahkan
Valgrind, GTK, dan Zig
- Sepanjang proses pengembangan, Valgrind digunakan untuk memverifikasi secara sistematis masalah seperti memory leak dan akses ke memori yang tidak terdefinisi
- Pemeriksaan Valgrind pada aplikasi GTK cukup rumit, dan memerlukan file suppression dalam jumlah besar (80% untuk GTK sendiri, sisanya untuk library pihak ketiga dan driver GPU)
- Dengan pemeriksaan berulang, bug memori kompleks yang hanya muncul pada kasus tertentu bisa ditemukan lebih awal
- Contoh: jika
WeakRefmilik GObject tidak diinisialisasi dengan benar, akses ke memori tak terdefinisi dapat terjadi ketika objek target dibebaskan nanti, dan ini bisa ditangkap lebih dulu oleh Valgrind
- Contoh: jika
- Berdasarkan pengalaman nyata, isu di dalam codebase Zig sendiri hanya berjumlah 2 kasus (1 leak, 1 akses tak terdefinisi), dan itu pun terjadi dalam proses integrasi dengan C API pihak ketiga
- Allocator debug milik Zig dan fitur integrasi dengan Valgrind juga terbukti efektif
- Isu memori lain yang ditemukan sebagian besar berasal dari boundary C API dan pengelolaan siklus hidup yang kompleks dalam sistem GObject
- Kesimpulannya, untuk menggunakan C API dari library kompleks dengan aman, diperlukan alat seperti Valgrind
- Fitur pendukung keamanan memori di Zig terbukti efektif bukan hanya dalam diskusi teoretis, tetapi juga melalui pengalaman proyek yang terverifikasi secara empiris
Kesimpulan
- Ini adalah pengalaman kelima membangun ulang bagian GUI Ghostty dari nol
- Secara berurutan: GLFW, macOS SwiftUI, macOS AppKit+SwiftUI, Linux GTK (prosedural), Linux GTK+sistem tipe GObject
- Dalam proses penulisan ulang yang berulang, setiap kali ada pelajaran baru dan pertumbuhan teknis yang didapat
- Pengalaman kali ini juga direncanakan untuk sebagian diterapkan pada proyek macOS
- Kolaborasi aktif dari tim pemelihara sistem Ghostty GTK juga ditekankan
- Aplikasi Ghostty GTK yang baru ditulis ulang kini telah menjadi default untuk build dari source, dan akan diterapkan pada rilis resmi 1.2
1 komentar
Komentar Hacker News
Saya belum pernah bekerja langsung dengan GTK, tetapi dari penjelasan Anda, rasanya ini sangat mirip dengan masalah yang saya hadapi saat membuat binding Godot dengan Zig. Godot punya sangat banyak konsep OOP seperti kelas, metode virtual, properti, sinyal, dan sebagainya. Selain itu, ada C API yang menangani semua konsep itu dan memungkinkan pembuatan objek serta properti kustom. Ia juga mengelola siklus hidup objek engine secara langsung, dan ada struktur pohon objek dengan reference counting. Khususnya saat mencoba membungkus masalah lifetime ini menjadi API optimal yang sesuai idiom Zig, kompleksitasnya jadi luar biasa. Karena memikirkan hal-hal ini, saya juga membuat library oopz. Status API-nya saat ini masih seperti itu, dan contoh nyatanya bisa dilihat di sini. Saya juga ingin mencoba membuat frontend Ghostty sebagai ekstensi Godot
Ini contoh yang bagus bahwa pemrograman yang baik pada akhirnya berarti menyesuaikan diri dengan cara kerja yang disediakan sistem. Apa pun pandangan kita tentang OOP atau manajemen memori, kalau memakai GTK maka mau tak mau harus merancang antarmuka dengan sistem tipe GObject dalam satu bentuk atau lainnya. Sekalipun ingin menghindarinya, pada akhirnya memang tidak bisa dihindari. Tetapi kami mencoba menghindarinya, dan hasilnya terjadi kekacauan besar saat mengikat lifetime objek yang reference-counted dengan objek yang tidak reference-counted. Di aplikasi Ghostty GTK, bug terus berulang di mana saat memori Zig dibebaskan, memori GTK tidak dibebaskan, atau sebaliknya
Terlepas dari sikap saya soal OOP dan manajemen memori, saya setuju bahwa jika memakai GTK maka Anda pasti akan terjerat dengan sistem tipe GObject. Karena itu saya memutuskan untuk sama sekali tidak memakai GTK secara langsung. Saya paham nilai dari tema UI yang seragam, tetapi menurut saya kelebihan GTK tidak cukup besar untuk layak dibayar dengan biaya seperti itu. Dari pengalaman saya menyentuh bagian pinggiran GTK di aplikasi open source, saya jadi yakin bahwa cara pandang GTK dan GObject tidak cocok dengan kecenderungan saya. Saya tidak benci GTK itu ada. Saya baik-baik saja memilih untuk tidak memakainya, tetapi anehnya sebagian orang tampaknya tidak menganggap pilihan itu sebagai hak saya. Ini hanya salah satu dari sekian banyak GUI toolkit, dan meskipun secara teknis sangat rapi, kalau pangsa GTK sedikit lebih kecil mungkin polesan itu bisa diarahkan ke toolkit lain yang secara struktural lebih baik. Tentu saja, apa yang saya anggap baik belum tentu baik bagi semua orang. Saya penasaran, dari orang-orang yang memakai GTK, berapa banyak yang sebenarnya terpaksa memakainya, dan berapa banyak yang merasa ini toolkit terbaik
Fakta menariknya, di Ghostty dan beberapa aplikasi GTK lain ada gejala klik scroll pertama diabaikan ketika mouse keluar dari jendela lalu masuk lagi. Ini akibat bug yang sangat tua, pertama kali dilaporkan pada 2015. Tautan bug. Sampai sekarang tidak ada rencana memperbaikinya, dan posisi maintainer adalah menunggu Wayland
Pada bagian “saya memverifikasi semua langkah dengan Valgrind”, sebenarnya ini terdengar sangat wajar, tetapi saya sendiri belum pernah benar-benar melakukannya, dan juga jarang melihat pengembang lain melakukannya. Biasanya Valgrind hanya dipakai saat bug tertentu atau penurunan performa sudah muncul. Kalau alat seperti Valgrind, khususnya Memcheck dan Helgrind, dipakai secara aktif sepanjang proses pengembangan, kualitas tool tampaknya bisa meningkat drastis, dan bug juga bisa ditangkap tepat saat diperkenalkan, sehingga tidak perlu repot menelusuri ratusan commit setelah kejadian
Saat memakai Ghostty, sangat mengganggu bahwa menempel banyak baris ke nano di Mac tidak bekerja dengan baik. Sepertinya ini tergantung bagaimana terminal menangani “bracketed pasting”, tetapi anehnya di iterm2 atau term masalah ini tidak ada
Saya penasaran apakah kalau dipakai Rust alih-alih Zig, error memori bisa dicegah. Karena kebanyakan masalah muncul dari interaksi Zig/C, mungkin dengan Rust hasilnya akan mirip. Sebagai pengembang Go saya hanya menebak, tetapi saya juga penasaran apakah ada bahasa yang memberi lebih banyak alat keselamatan saat harus berintegrasi besar-besaran dengan C
Saat memakai aplikasi berbasis GPU seperti Ghostty, Alacritty, WezTerm, Zed, dan lainnya, saya memang merasa lebih cepat dan lebih baik. Tetapi ironisnya, aplikasi semacam ini justru memperlihatkan dengan lebih jelas keterbatasan driver Nvidia. Dulu saya tidak sadar karena hampir tidak pernah memakai GPU, tetapi baik di lingkungan tanpa compositor seperti Regolith i3wm maupun di sway/wayland, driver nvidia benar-benar buruk dalam hal screen sharing, bangun dari sleep, crash, dan sebagainya. Saya sudah mencoba beberapa versi berbeda (550/560/575/580) dan semuanya sama saja. Baru belakangan ini saya sadar ternyata sudah seburuk ini sejak lama
Saya pernah bisa membuat satu aplikasi besar tanpa membiarkan sistem tipe GTK memengaruhi kode saya. Tetapi sebagai gantinya, alih-alih memakai pewarisan kelas atau ekstensi, saya menghubungkan semua komponen hanya dengan binding lambda. Hasil akhirnya tidak terlalu berantakan, tetapi bagi pengembang yang terbiasa dengan gaya GTK yang ortodoks mungkin akan terasa membingungkan
Saya sendiri tidak mengerti hype berlebihan terhadap Ghostty. UI-nya hanya punya tab dan menu konteks, jadi saya ragu apakah integrasi dan rewrite seperti ini benar-benar layak. Saya menduga mereka mungkin ingin menambahkan lingkungan GUI yang kuat seperti iterm2. Kitty menggambar tab sendiri dengan OpenGL sehingga bisa dikustomisasi penuh, dan karena tidak membuang waktu untuk integrasi dengan framework yang rumit, ia bisa lebih cepat menghadirkan fitur yang sangat praktis seperti membungkus hasil perintah terakhir ke pager untuk ditampilkan. Kitty juga mendukung remote dengan baik