This shows you the differences between two versions of the page.
ii:labs:s2:02:tasks:02 [2022/04/15 02:15] radu.mantu |
ii:labs:s2:02:tasks:02 [2024/03/17 18:24] (current) florin.stancu |
||
---|---|---|---|
Line 1: | Line 1: | ||
- | ==== 02. [??p] Writing the frame buffer ==== | + | ==== 02. [20p] Hyper-linking multiple pages ==== |
- | Continuing from where we left off in Ex. 1, reading /dev/fb0 may or may not yield the screenshot we expected. If it didn't, the culprit here is most likely the X server, the application that is called upon to draw graphic elements (e.g.: windows) on your screen. When active, **Xorg** (the X server variant that you're most likely to have installed) places /dev/fb0 under a write lock, meaning that all other processes are prohibited from writing to it. This can be verified with **lslock** (shows file locks) or **lsof** (shows opened files). | + | Time to add a second HTML page: |
- | However, if your script did not generate the desired screenshot but in stead some boot-time messages (related to filesystems and whatnot), then /dev/fb0 is probably available for writes. So what gives? How does the X server render your GUI without writing to the frame buffer? The answer is that it's using a frame buffer, but not //that// frame buffer. /dev/fb0 is the most primitive frame buffer that's available. That's the frame buffer used by BIOS or UEFI, way before your OS is even loaded in RAM. That's the //slow// frame buffer. If you have a GPU, then the X server is using //its// frame buffer, and that frame buffer takes precedence over /dev/fb0. | + | * As expected, copy + paste your initial HTML with a new name, e.g.: ''second.html''; |
+ | * Link together the two ''.html'' pages (in each HTML page, find the ''<a class="..." href="..">'' links inside the ''#navbarToggle'' ''div'' and edit the ''href''s to point to each-other); | ||
- | <code bash> | + | <note warning> |
- | # check whether Xorg is using the native frame buffer or the GPU frame buffer in /dev/dri | + | Use relative paths, either ''pagename.html'' or ''./pagename.html'' will do! **DO NOT USE:** absolute paths, e.g., ''C:\Users\...\pagename.html'' for obvious portability reasons! |
- | $ sudo lsof | grep -e '/dev/fb[0-9]*' -e '/dev/dri/card[0-9]*' | grep Xorg | + | </note> |
- | </code> | + | |
- | So then, how do we write the frame buffer? That was the point of the exercise, remember? If the X server is using /dev/fb0, we can't use it since it's locked. If the X server is using /dev/dri/card0, we can use /dev/fb0 but it's pointless. Well, we have a solution for that, but first we need to understand what is a [[https://www.howtogeek.com/428174/what-is-a-tty-on-linux-and-how-to-use-the-tty-command/|teletype (TTY)]]. | + | * Test by opening the HTML files in your browser. The user should be able to navigate between the two pages seamlessly! |
- | + | * One final touch: we want the menu to highlight (i.e., change foreground / background color and font style) the currently active menu button (different for each page)! | |
- | [[https://ocw.cs.pub.ro/courses/_media/ii/labs/s2/02/tasks/teletype.jpeg|{{ :ii:labs:s2:02:tasks:teletype.jpeg?500 |}}]] | + | |
- | + | ||
- | Back in the old days, there were no displays. Not even CRT monitors. When computers started being fast enough to be considered "interactive", people wanted to have a way to instantly communicate with them. Their solution was to hook said computers up to a teletype writer (see image above), meaning that the terminal output was actually written on paper! In time, the peripherals that we're used to today came to be and the TTY became an abstraction of the old writers (i.e.: unified I/O devices). | + | |
- | + | ||
- | As a result, the TTY today can be considered a virtual device that's not hooked to a mechanical keyboard and a roll of paper but might as well be for all we care. Being virtualized, it means that the OS can provide us with as many as we want. And it does. Right now, you are logged in in **tty1**. The OS provides a basic driver for all TTYs. This driver can echo keys to the screen when you press them, or go to a new line when you hit enter. All the good stuff... But you're too good for that. You wanted a GUI and decided to use the X server. The X server usurped the clean environment that the OS provided and replaced it with colorful pixels. The terminal you're writing in now is not even a real terminal. It's not a TTY. See for yourself: | + | |
- | + | ||
- | <code bash> | + | |
- | # print the name of the terminal that's connected to the standard input | + | |
- | $ tty | + | |
- | /dev/pts/1 | + | |
- | </code> | + | |
- | + | ||
- | That **pts** there stands for Pseudo-Terminal Slave. A PTS is a userspace program that tries to emulate a TTY, including its driver. On Ubuntu, that program is most likely **gnome-terminal**. Other examples include Terminator, Alacritty or Kitty. While the terminal emulator tries to recreate the functionality of the original TTY, it relies on Xorg to render each character in it's assigned window, pixel by pixel. And Xorg does this by hiding the true TTY (i.e.: tty1) by directly accessing the frame buffer (either one of them). While Xorg is still alive, we won't be able to accomplish our goal. So is this the solution? Are we killing Xorg? Not necessarily: we can just move to tty2... Xorg is bound to tty1 by default, but the OS can make up as many TTYs as we want. Let's do that: | + | |
- | + | ||
- | <code bash> | + | |
- | # switch the system to tty2 (also works with <Ctrl + Alt + F2>) | + | |
- | $ sudo chvt 2 | + | |
- | + | ||
- | # tty2 doesn't know about GPUs; it uses /dev/fb0 | + | |
- | # Xorg is sleeping since we're not using tty1 | + | |
- | $ sudo dd if=/dev/urandom of=/dev/fb0 | + | |
- | + | ||
- | # chaos ensues | + | |
- | + | ||
- | # clean up with <Ctrl + L> or | + | |
- | $ clear | + | |
- | + | ||
- | # return to a more familiar sight (<Ctrl + Alt + F1>) | + | |
- | $ sudo chvt 1 | + | |
- | </code> | + | |
- | + | ||
- | We've just confirmed that while in tty2, we can safely (and successfully) write to /dev/fb0. Moreover, once we switch back to tty1, if Xorg uses the GPU and bypasses the native framebuffer, /dev/fb0 will contain the last image rendered while in tty2. If you're running Linux in a VM this is unlikely, but not an issue. So here's the task: | + | |
- | + | ||
- | === [??p] Task A - Write img2fb.py === | + | |
- | + | ||
- | This new script will have to do the exact opposite of **fb2img.py**. The script will take an image of your choosing and write it to the frame buffer (you know which one). Also, it must take the following arguments: | + | |
- | + | ||
- | <code> | + | |
- | positional arguments: | + | |
- | FILE input image file | + | |
- | + | ||
- | options: | + | |
- | -h, --help show this help message and exit | + | |
- | --dst /dev/fb* data destination | + | |
- | --width INT screen width [px] | + | |
- | --height INT screen height [px] | + | |
- | --hoff INT horizontal offset [px] | + | |
- | --voff INT vertical offset [px] | + | |
- | </code> | + | |
- | + | ||
- | The result should look something like this: | + | |
- | + | ||
- | [[https://ocw.cs.pub.ro/courses/_media/ii/labs/s2/02/tasks/lk_screen.png|{{ :ii:labs:s2:02:tasks:lk_screen.png?700 |}}]] | + | |
<note tip> | <note tip> | ||
- | Here's an easy way to test your script without constantly switching between tty1 and tty2: | + | **Hint**: create a CSS class (e.g., ''.active'') with your desired properties and append it to the appropiate menu link element: ''<a class="nav-item nav-link" ...>'' (also note: different class attribute values are separated by space!). |
+ | </note> | ||
- | <code bash> | ||
- | # create a copy of /dev/fb0 in persistent storage & change ownership to yourself | ||
- | $ sudo cp /dev/fb0 frame_buffer.bin | ||
- | $ sudo chown student !$ | ||
- | |||
- | # create a backup with the .bak extension | ||
- | $ cp frame_buffer.bin{,.bak} | ||
- | |||
- | # test img2fb.py with the persistent copy as destination | ||
- | $ ./img2fb.py --dst frame_buffer.bin nyan_cat.jpg | ||
- | |||
- | # extract an actual image from the modified copy of the framebuffer | ||
- | $ ./fb2img.py --src frame_buffer.bin does_this_look_good.png | ||
- | |||
- | # you screwed up: revert changes and try again | ||
- | $ cp frame_buffer.bin{.bak,} | ||
- | </code> | ||
- | </note> |