<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Oleg</title>
    <description>The latest articles on Forem by Oleg (@olegelantsev).</description>
    <link>https://forem.com/olegelantsev</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F178681%2F811c3fb5-076c-423a-a0c2-b76234ba0eb6.jpeg</url>
      <title>Forem: Oleg</title>
      <link>https://forem.com/olegelantsev</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/olegelantsev"/>
    <language>en</language>
    <item>
      <title>Revisiting an Old Project — and the Endless Spiral of Dependencies</title>
      <dc:creator>Oleg</dc:creator>
      <pubDate>Mon, 13 Oct 2025 04:50:10 +0000</pubDate>
      <link>https://forem.com/olegelantsev/revisiting-an-old-project-and-the-endless-spiral-of-dependencies-4gf6</link>
      <guid>https://forem.com/olegelantsev/revisiting-an-old-project-and-the-endless-spiral-of-dependencies-4gf6</guid>
      <description>&lt;p&gt;I wanted to make a simple post about an old project I once worked on. And this one is not the one I intended to make.&lt;/p&gt;

&lt;p&gt;To write the post, I needed to make a screenshot.&lt;br&gt;
To take that screenshot, I had to rebuild the project.&lt;/p&gt;

&lt;p&gt;This project is rarely touched, and it was originally started with Ant and Eclipse — when there was no Android Studio yet. No surprise the IDE didn’t like the Gradle version 5.4.1, and insisted on 8.5. This started my Saturday evening journey to make a screenshot of the app…&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmx26w79j2nsw1qon70w9.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmx26w79j2nsw1qon70w9.webp" alt="Gradle version upgrade" width="800" height="219"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Gemini CLI didn’t help a lot with the upgrade unfortunately. I spend around 20 minutes with upgrade of Gradle, configuration, and library versions. This wasn’t that bad as moving from Ant to Gradle :-D&lt;/p&gt;

&lt;p&gt;The project was now successfully built… but something wasn’t right at the runtime. So I had to debug.&lt;/p&gt;

&lt;p&gt;Android Studio, however, decided to fight back. It wouldn’t show Java variables at breakpoints until it finished indexing all files — including every single C++ file, even the ones outside the project. I increased the IDE max heap size to 5 GB — still no luck.&lt;/p&gt;

&lt;p&gt;Come on, Android Studio, I don’t need C++ symbols indexed; I just want to see my Java variables!&lt;/p&gt;

&lt;p&gt;Then I realized what was taking the entire indexing effort — my boost dependency. I realized that indeed, I used boost::filesystem for file operations, and the IDE was trying to index the entire Boost library, all the headers references in the Android.mk file.&lt;/p&gt;

&lt;p&gt;“Fine,” I thought. “Let’s just remove Boost — C++17 already has std::filesystem. I’ll update the code.”&lt;/p&gt;

&lt;p&gt;I asked Gemini CLI agent to remove the boost dependency with std::filesystem , it immediately burned my daily quota, and actually wasn’t even half done. I replaced it rather quickly myself with grep and sed. And also upgraded the compiler to C++17… and got an error about boost/geometry, which I’d completely forgotten was also there.&lt;br&gt;
So I added Boost back to Android.mk.&lt;/p&gt;

&lt;p&gt;This time, the build failed with a mysterious message:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;error: no template named 'unary_function' in namespace 'std';
did you mean '__unary_function'?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Turns out my old Boost (1.69.0) doesn’t play well with C++17. I needed at least Boost 1.75.0.&lt;br&gt;
Alright — time to update Boost.&lt;/p&gt;

&lt;p&gt;Except… the Boost website no longer offers simple source archives (as it was back in 2014). Now there are only platform-specific .tar.gz bundles for Unix &amp;amp; Windows. So I went to GitHub and found the boost is modularized into independent repos (with umbrella repo present that includes everything with submodules). However, the point was to use only what is necessary to reduce the burden of Android Studio indexer, so I cloned boostorg/geometry and checked out “boost-1.75.0” tag.&lt;/p&gt;

&lt;p&gt;What I got next was:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;geometry.hpp:24:10: fatal error: 'boost/config.hpp' file not found
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Basically, boost::geometry depends on the boost::config. No problem, lets do the same trick with the “boostorg/config”. Then another error of missing header of other lib. I ended up cloning assert, config, core, geometry,math,throw_exception … and the list kept expanding. Nah. This would not help me much if I have to include the whole boost lib again.&lt;/p&gt;

&lt;p&gt;What was I doing…&lt;/p&gt;

&lt;p&gt;Eventually, I rolled back the boost upgrade changes, and decided to increase the IDE heap size to the max 8192MB, and there were no indexing anymore. Breakpoint worked, and debugger showed me variable values.&lt;/p&gt;

&lt;p&gt;All I wanted was a screenshot.&lt;br&gt;
Hours later, I finally re-assembled the project and fixed the bug, learned about Boost modular repositories on Github, and questioned every career choice I’ve ever made ;-)&lt;/p&gt;

</description>
      <category>android</category>
      <category>devjournal</category>
      <category>tooling</category>
    </item>
    <item>
      <title>Made a game in C++ no engines used</title>
      <dc:creator>Oleg</dc:creator>
      <pubDate>Thu, 12 Dec 2024 11:57:01 +0000</pubDate>
      <link>https://forem.com/olegelantsev/made-a-game-in-c-no-engines-used-4om5</link>
      <guid>https://forem.com/olegelantsev/made-a-game-in-c-no-engines-used-4om5</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fugafanxlf3s53dysgvsx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fugafanxlf3s53dysgvsx.png" alt=" " width="800" height="498"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The game I created 12 years ago, written in C++, no game engine, built for Android and iOS.&lt;/p&gt;

&lt;p&gt;Recently I dug it from my dusty projects closet and decided to bring it back to life. To measure how much dust — let’s see what changed since then? Ant is no longer used as build system for Android, Apple released Swift and Metal (luckily OpenGL ES still works), Google released Kotlin, new IDE as well… Getting it compiling again was a challenge, but I guess this story worth a separate post. Now the game is available in the AppStore (link).&lt;/p&gt;

&lt;p&gt;Next — I will share some insights from game development process, why it was built, what tools I used, level builders, etc.&lt;/p&gt;

&lt;p&gt;Subscribe to not miss it&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/y1cKoA7DhP8"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Read Committed transaction isolation in PostgreSQL</title>
      <dc:creator>Oleg</dc:creator>
      <pubDate>Thu, 04 Jul 2024 15:13:47 +0000</pubDate>
      <link>https://forem.com/olegelantsev/read-committed-transaction-isolation-in-postgresql-3c4d</link>
      <guid>https://forem.com/olegelantsev/read-committed-transaction-isolation-in-postgresql-3c4d</guid>
      <description>&lt;p&gt;Recently I was reading “Designing Data-Intensive Applications” book. It covers lots of topics related to creation of reliable, scalable and maintainable data systems and one of the chapter covers a concept of transactions - its meaning, use cases, levels of isolation and reasons why application developers should choose the right one. While many books and articles cover transaction isolation level with pure SQL examples for database users, I decided to go a bit deeper and check how a theory gets connected with actual implementation. As a software engineer with C++ background, it's interesting to check out the source code of one of the widely used open source databases - PostgreSQL. According to Statista it takes 4th place in most popular databases 2023. Typically first go Microsoft SQL, MySQL, Oracle and PostgreSQL.&lt;/p&gt;

&lt;p&gt;To make the study interesting and constructive and limit the study scope, I will try to answer a question - how does transaction isolation work in general, and how database maintains READ COMMITTED isolation level in particular?&lt;/p&gt;

&lt;p&gt;There are 4 transaction isolation levels in PostgreSQL:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;DIRTY READ&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;READ COMMITTED&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;REPEATABLE READ&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;SERIALIZABLE&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Read committed level guarantees that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;transaction will see only committed results (no dirty reads). Transaction won’t see uncommitted changes of other concurrently running transactions&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;writing to the database will overwrite only committed data (no dirty writes)&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's start with some SQL code and illustrations, and then explore the source code. First create a simple table:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CREATE TABLE public.employees (
  id SERIAL PRIMARY KEY,
  name VARCHAR NOT NULL,
  surname VARCHAR NOT NULL,
);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One row is sufficient for our case:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;INSERT INTO accounts ("name", "surname") VALUES ("Phil", "Smith")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lets run the following magic query&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# SELECT xmin, xmax, ctid, * FROM employees;
  xmin  | xmax | ctid  | id | name | surname 
--------+------+-------+----+------+---------
 218648 |    0 | (0,1) |  1 | Phil | Smith
(1 row)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Interesting numbers are in the response. Those are initial tuple attribute values for the inserted record of the first employee, but lets run two other transactions A and B concurrently (or sequentially for simplicity):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;-- transaction A
BEGIN;
UPDATE employees SET name = "Bob" WHERE id = 1;
COMMIT;

-- transaction B
BEGIN;
SELECT name FROM employees WHERE id = 1;
SELECT name FROM employees WHERE id = 1;
COMMIT;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and run the magic query again&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# SELECT xmin, xmax, ctid, * FROM employees;
  xmin  | xmax | ctid  | id | name | surname 
--------+------+-------+----+------+---------
 218651 |    0 | (0,2) |  1 | Bob  | Smith
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;xmin&lt;/code&gt; has slightly increased, and ctid has changed as well. Those are quite a clue for understanding how MVCC works. But let's take a step back and try to see what happens when those transactions run concurrently, i.e. what names become visible to transaction B.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq934hhny6xllna5ewl48.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq934hhny6xllna5ewl48.png" alt="transactions" width="800" height="254"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The diagram above illustrates non-repeatable read problem with read-committed guarantee. Transaction B accesses the seemingly same row twice. First time SELECT reads the value after the transaction A executed an update on that row, but before that change was committed. READ COMMITTED isolation level prevents dirty (uncommitted) reads. Next in a timeline, transaction A commits the changes and transaction B accesses that row again. This time it will read an updated row, or actually a new tuple. By the way this may be dangerous in certain contexts, e.g. financial domain when performing the changes in account’s balance, thus for those tasks a stronger isolation might be needed.&lt;/p&gt;

&lt;h2&gt;
  
  
  So how does transaction isolation work in PostgreSQL?
&lt;/h2&gt;

&lt;p&gt;Without concurrency control, client may see inconsistent half-written data. The  way to guarantee consistent view is by introducing isolation for the requests, and the basic approach is through adding table level read-write locks, but such locks create a high level of contention between read and update transactions, resulting in very slow access. Here comes MVCC that allows concurrent read and write operations without locking the entire table. It may implement multiple levels of isolation, and Read Committed is a default one in PostgreSQL. What a particular transaction sees depends on its isolation level. With read committed level transactions see a snapshot of the database at the start of each transaction, i.e. see point-in-time consistent views. This isolation is achieved through data versioning - due to concurrent transactions there might be multiple versions of rows at a time visible to different transactions. Each row modification is associated with transaction, that either created, modified or deleted row. Each row also contains a visibility marker, indicating when row is visible and when it becomes invisible (after deletion or update). Isolation provides guarantees for concurrent data access.&lt;/p&gt;

&lt;h2&gt;
  
  
  How exactly does MVCC track versions?
&lt;/h2&gt;

&lt;p&gt;Remember that magic query we ran and the produced results? Let's try to understand what those mean and have a look at tuple declaration in http_details.h. HeapTupleFields catches my attention because it contains t_xmin, t_xmax and t_infomask from HeapTupleHeaderData also seems an important attribute - they seem to provide tuples with the necessary versioning, visibility levels required for transaction isolation. There are more attributes of course, but these must be describing the basic cases.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;typedef struct HeapTupleFields
{
  TransactionId t_xmin; /* inserting xact ID */
  TransactionId t_xmax; /* deleting or locking xact ID */
  union
  {
     CommandId t_cid; /* inserting or deleting command ID, or both */
     TransactionId t_xvac; /* old-style VACUUM FULL xact ID */
  } t_field3;
} HeapTupleFields;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From the same header I read that&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;t_infomask&lt;/code&gt; is uint16 attribute and among other things it has bits enriching the &lt;code&gt;xmin&lt;/code&gt; and &lt;code&gt;xmax&lt;/code&gt; values, describing whether those are COMMITTED, INVALID or FROZEN.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;xmin&lt;/code&gt; - is used to store transaction XID, assigned at INSERT and UPDATE commands&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;xmax&lt;/code&gt; - kinda invalidating or expiring a tuple, set by UPDATE or DELETE. It might be also used for row locks&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What happens when row gets updated while being repeatably read by another transaction?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flefqbioneub3phvn717c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flefqbioneub3phvn717c.png" alt="snapshot data" width="800" height="337"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Create new tuple
&lt;/h3&gt;

&lt;p&gt;When a transaction updates a tuple, it actually creates a new version of it (AKA copy-on-write approach) with its own XID and New tuple's header has XMin value equal to transaction XID - 218648.&lt;/p&gt;

&lt;h3&gt;
  
  
  Mark old tuple for deletion
&lt;/h3&gt;

&lt;p&gt;The old tuple's Xmax (Transaction ID of the last transaction to modify the tuple) is set to the XID of the updating transaction. Future transactions should not be able to see it (at least after commit). When tuple has non-zero xmax may actually have a few interpretations depending on the other flags set in tuple header (e.g. xmax is also assigned when tuple is locked for update), but for simplicity I will omit those details.&lt;/p&gt;

&lt;h3&gt;
  
  
  Vacuum old tuple
&lt;/h3&gt;

&lt;p&gt;The Xmin and Xmax values are also used during the garbage collection process to determine which tuples are no longer visible to any active transaction. Tuples with an Xmax that is less than the current transaction's XID and when no other current transactions access the old tuple (e.g. deleted or replaced) - they can be safely removed during vacuuming.&lt;/p&gt;

&lt;p&gt;There are more interesting things happen to the states of tuples, transactions and snapshots. For example, tuple state and its visibility is well described in &lt;a href="https://github.com/postgres/postgres/blob/master/src/backend/access/heap/heapam_visibility.c" rel="noopener noreferrer"&gt;heapam_visibility.c&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why don't concurrent transactions see new tuple until XID 218649 is committed?
&lt;/h2&gt;

&lt;p&gt;At a start up of each transaction, one requests a snapshot object, containing information about all running transactions. Let's have a look at procarray.c file and very descriptive comment of GetSnapshotData function:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs15h724aiyalxnpd0o9m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs15h724aiyalxnpd0o9m.png" alt="get snapshot data" width="800" height="337"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Having a snapshot object describing currently running transactions and XIDs "boundaries" is quite helpful for complying with READ COMMITTED isolation level. From the example above, no other transaction of such isolation level will see "name = Bob" until transaction 3 is successfully committed.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/postgres/postgres/blob/master/src/backend/access/heap/heapam_visibility.c" rel="noopener noreferrer"&gt;heapam_visibility.c&lt;/a&gt; seems to be the right place to look for understanding how a snapshot type impacts the tuple visibility for transaction.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo4wpak7ryck5xx1knkug.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo4wpak7ryck5xx1knkug.png" alt="tuple visibility" width="800" height="542"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Those functions rely on tuple header fields (Xmin, Xmax and a few flag values), snapshot object to determine the visibility of the tuple to other transactions.&lt;/p&gt;

&lt;h2&gt;
  
  
  The end
&lt;/h2&gt;

&lt;p&gt;It was surprisingly fun to read PostgreSQL source code. Git blame reported that some lines were more than 20 years old. Methods frequently are well documented with comments and not only describe what a particular function does, but also conditions under which this function must be invoked, what kind of locks it uses, what optimisations were tried before, etc. For those who wants to dig deeper and explore the tuple states I recommend to start with &lt;a href="https://github.com/postgres/postgres/blob/cca97ce6a6653df7f4ec71ecd54944cc9a6c4c16/src/include/access/htup_details.h" rel="noopener noreferrer"&gt;htup_details.h&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>postgres</category>
      <category>database</category>
    </item>
    <item>
      <title>Linux TTY on LCD 16x2 screen</title>
      <dc:creator>Oleg</dc:creator>
      <pubDate>Sun, 04 Dec 2022 11:03:50 +0000</pubDate>
      <link>https://forem.com/olegelantsev/linux-tty-on-lcd-16x2-screen-176k</link>
      <guid>https://forem.com/olegelantsev/linux-tty-on-lcd-16x2-screen-176k</guid>
      <description>&lt;p&gt;Yet another just for fun project. I decided to make use of a small LCD screen from Arduino kit &amp;amp; Raspberry Pi.&lt;/p&gt;

&lt;p&gt;Inspired by historical teletype access to the computers &amp;amp; also challenges to use low-end tech as working setup (e.g. full day work on Raspberry Pi or Chrome books). Even today terminal is used, mostly by software developers, to run system commands for managing files, writing code, debugging, building apps, managing configs, etc. It’s sometimes surprising how much can be done by using only terminal and no fancy GUI at all.&lt;/p&gt;

&lt;p&gt;What I want - is to use see TTY in LCD screen as if it is the only screen available.&lt;/p&gt;

&lt;h3&gt;
  
  
  Breadboard
&lt;/h3&gt;

&lt;p&gt;There are many articles available on how to connect LCD 1602 to Raspberry Pi (e.g. &lt;a href="https://www.mbtechworks.com/projects/drive-an-lcd-16x2-display-with-raspberry-pi.html" rel="noopener noreferrer"&gt;this&lt;/a&gt;). The only component I miss is the trimpot for contrast. I replaced it with 1K Ohm resistor and contrast turned out to be about right.&lt;/p&gt;

&lt;p&gt;Once the LCD is connected, a simple program in Python will be able to render characters. I used a program from an article as a scaffold and quickly rendered first “Hello, World!”.&lt;/p&gt;

&lt;h3&gt;
  
  
  TTY
&lt;/h3&gt;

&lt;p&gt;TTY stands for teletype. It's a user interface device that uses text for input and output. On Linux if you run &lt;code&gt;tty&lt;/code&gt; command, it returns user’s terminal name. The original terminals in Linux are &lt;code&gt;/dev/ttyX&lt;/code&gt; - as I understood those are available with CTRL + ALT + F1 console.&lt;/p&gt;

&lt;p&gt;There are also &lt;code&gt;/dev/pty/&lt;/code&gt; that functions as a pseudo-TTY - that acts like physical, but is not a real one. If you open a term app in Ubuntu or SSH to it and run &lt;code&gt;tty&lt;/code&gt; command output will show it’s a pseudo-terminal.&lt;/p&gt;

&lt;p&gt;The greatest article I can recommend on TTY - &lt;a href="http://www.linusakesson.net/programming/tty/index.php" rel="noopener noreferrer"&gt;The TTY demystified&lt;/a&gt;. I am also looking for more resource to learn more on TTY subsystem. Any sources you can recommend?&lt;/p&gt;

&lt;p&gt;The next task - is to read the TTY in Linux and pass it to the LCD.&lt;/p&gt;

&lt;p&gt;I was sure there should be a way to access it and was considering writing a TTY driver for it, but came across &lt;a href="https://www.reddit.com/r/raspberry_pi/comments/fnd8rk/full_linux_tty_on_lcd1602/" rel="noopener noreferrer"&gt;this Reddit post&lt;/a&gt; that exactly what I want and mentions VCS.&lt;/p&gt;

&lt;p&gt;VCS - &lt;a href="https://man7.org/linux/man-pages/man4/vcs.4.html" rel="noopener noreferrer"&gt;man vcs&lt;/a&gt; - virtual console memory&lt;/p&gt;

&lt;p&gt;Example of its contents:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;$&lt;/span&gt; &lt;span class="nx"&gt;hexdump&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;dev&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;vcs2&lt;/span&gt;
&lt;span class="mi"&gt;0000000&lt;/span&gt;
&lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="mi"&gt;00001&lt;/span&gt;&lt;span class="nx"&gt;e0&lt;/span&gt;   &lt;span class="nx"&gt;R&lt;/span&gt;   &lt;span class="nx"&gt;a&lt;/span&gt;   &lt;span class="nx"&gt;s&lt;/span&gt;   &lt;span class="nx"&gt;p&lt;/span&gt;   &lt;span class="nx"&gt;b&lt;/span&gt;   &lt;span class="nx"&gt;i&lt;/span&gt;   &lt;span class="nx"&gt;a&lt;/span&gt;   &lt;span class="nx"&gt;n&lt;/span&gt;       &lt;span class="nx"&gt;G&lt;/span&gt;   &lt;span class="nx"&gt;N&lt;/span&gt;   &lt;span class="nx"&gt;U&lt;/span&gt;   &lt;span class="o"&gt;/&lt;/span&gt;   &lt;span class="nx"&gt;L&lt;/span&gt;   &lt;span class="nx"&gt;i&lt;/span&gt;   &lt;span class="nx"&gt;n&lt;/span&gt;
&lt;span class="mi"&gt;00001&lt;/span&gt;&lt;span class="nx"&gt;f0&lt;/span&gt;   &lt;span class="nx"&gt;u&lt;/span&gt;   &lt;span class="nx"&gt;x&lt;/span&gt;       &lt;span class="mi"&gt;1&lt;/span&gt;   &lt;span class="mi"&gt;1&lt;/span&gt;       &lt;span class="nx"&gt;r&lt;/span&gt;   &lt;span class="nx"&gt;a&lt;/span&gt;   &lt;span class="nx"&gt;s&lt;/span&gt;   &lt;span class="nx"&gt;p&lt;/span&gt;   &lt;span class="nx"&gt;b&lt;/span&gt;   &lt;span class="nx"&gt;e&lt;/span&gt;   &lt;span class="nx"&gt;r&lt;/span&gt;   &lt;span class="nx"&gt;r&lt;/span&gt;   &lt;span class="nx"&gt;y&lt;/span&gt;   &lt;span class="nx"&gt;p&lt;/span&gt;
&lt;span class="mi"&gt;0000200&lt;/span&gt;   &lt;span class="nx"&gt;i&lt;/span&gt;       &lt;span class="nx"&gt;t&lt;/span&gt;   &lt;span class="nx"&gt;t&lt;/span&gt;   &lt;span class="nx"&gt;y&lt;/span&gt;   &lt;span class="mi"&gt;2&lt;/span&gt;
&lt;span class="mi"&gt;0000210&lt;/span&gt;
&lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="mi"&gt;00005&lt;/span&gt;&lt;span class="nx"&gt;a0&lt;/span&gt;   &lt;span class="nx"&gt;r&lt;/span&gt;   &lt;span class="nx"&gt;a&lt;/span&gt;   &lt;span class="nx"&gt;s&lt;/span&gt;   &lt;span class="nx"&gt;p&lt;/span&gt;   &lt;span class="nx"&gt;b&lt;/span&gt;   &lt;span class="nx"&gt;e&lt;/span&gt;   &lt;span class="nx"&gt;r&lt;/span&gt;   &lt;span class="nx"&gt;r&lt;/span&gt;   &lt;span class="nx"&gt;y&lt;/span&gt;   &lt;span class="nx"&gt;p&lt;/span&gt;   &lt;span class="nx"&gt;i&lt;/span&gt;       &lt;span class="nx"&gt;l&lt;/span&gt;   &lt;span class="nx"&gt;o&lt;/span&gt;   &lt;span class="nx"&gt;g&lt;/span&gt;   &lt;span class="nx"&gt;i&lt;/span&gt;
&lt;span class="mi"&gt;00005&lt;/span&gt;&lt;span class="nx"&gt;b0&lt;/span&gt;   &lt;span class="nx"&gt;n&lt;/span&gt;   &lt;span class="p"&gt;:&lt;/span&gt;       &lt;span class="nx"&gt;o&lt;/span&gt;   &lt;span class="nx"&gt;l&lt;/span&gt;   &lt;span class="nx"&gt;e&lt;/span&gt;   &lt;span class="nx"&gt;g&lt;/span&gt;
&lt;span class="mi"&gt;00005&lt;/span&gt;&lt;span class="nx"&gt;c0&lt;/span&gt;
&lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="mi"&gt;0000780&lt;/span&gt;   &lt;span class="nx"&gt;P&lt;/span&gt;   &lt;span class="nx"&gt;a&lt;/span&gt;   &lt;span class="nx"&gt;s&lt;/span&gt;   &lt;span class="nx"&gt;s&lt;/span&gt;   &lt;span class="nx"&gt;w&lt;/span&gt;   &lt;span class="nx"&gt;o&lt;/span&gt;   &lt;span class="nx"&gt;r&lt;/span&gt;   &lt;span class="nx"&gt;d&lt;/span&gt;   &lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="mi"&gt;0000790&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;What my Python program does is pretty straightforward:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;continuously reads characters from &lt;code&gt;/dev/vcs&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;renders lines on LCD screen&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But entire TTY currently doesn’t fit the LCD screen, it has to be resized. &lt;code&gt;stty&lt;/code&gt; tool helps with that:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;stty&lt;/span&gt; &lt;span class="nx"&gt;cols&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt; &lt;span class="nx"&gt;rows&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;After that the output become correct and TTY is usable. &lt;/p&gt;
&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;I played around with terminal on such screen. Tasks such as &lt;code&gt;ls&lt;/code&gt; that needs multiple line output are better be used in combination with &lt;code&gt;less&lt;/code&gt; so you can conveniently scroll the content as there are only 2 rows available.&lt;/p&gt;

&lt;p&gt;I managed to create a simple C++ program that prints Hello World, compile and run it 😀 and a few commands in Python interpreter.&lt;/p&gt;

&lt;p&gt;Here is a short demo video:&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;a href="https://www.youtube.com/shorts/kIuDFnvjQvw?feature=share" rel="noopener noreferrer"&gt;
      youtube.com
    &lt;/a&gt;
&lt;/div&gt;



&lt;h3&gt;
  
  
  References:
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://www.mbtechworks.com/projects/drive-an-lcd-16x2-display-with-raspberry-pi.html" rel="noopener noreferrer"&gt;https://www.mbtechworks.com/projects/drive-an-lcd-16x2-display-with-raspberry-pi.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.reddit.com/r/raspberry_pi/comments/fnd8rk/full_linux_tty_on_lcd1602/" rel="noopener noreferrer"&gt;https://www.reddit.com/r/raspberry_pi/comments/fnd8rk/full_linux_tty_on_lcd1602/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>node</category>
      <category>cli</category>
      <category>architecture</category>
    </item>
  </channel>
</rss>
