In the previous article, I described how I experimented with FastCarPlay and LIVI, then modified the Carlinkit initialization procedure to achieve a stable phone connection.
But successfully connecting CarPlay is only part of the challenge. To use the system in a car, you also need convenient controls.
In my case, the primary input device was a touchscreen. And getting it to work properly was the next stage of development.
First Experiments with Touchscreen Controls
FastCarPlay already had support for touch input. However, in its original form, the project was largely oriented toward keyboard controls.
That is a perfectly reasonable approach for development and testing CarPlay. But I wanted to build a complete car head unit that could be operated directly through the screen, without a keyboard or mouse.
During my first experiments, I encountered unstable touchscreen behavior on my hardware.
As far as I remember, after a few touches or an attempt to swipe, CarPlay would stop responding to touch input. The application itself, however, continued running.
I can no longer say exactly what caused this behavior. It may have been related to my particular configuration, the hardware, or input event handling.
So it would be wrong to draw conclusions about the cause of the issue in the original project.
Besides, I was planning to create my own head unit interface anyway, which meant I would eventually need to adapt the input handling for CaRadio.
From Keyboard to Touchscreen
FastCarPlay used SDL, a library I started exploring after returning to the project.
One of its advantages is that it can receive events from different input devices and process them directly within the application.
This was convenient for my head unit.
I could use a mouse while developing on a computer and a touchscreen when running the software on Raspberry Pi. The main control logic could remain the same.
But for CarPlay, simply detecting that someone touched the display is not enough.
The application needs to know exactly where the touch occurred, when the finger touched the screen, whether it moved, and when it was released.
All these actions must be converted into commands that CarPlay can understand.
Taps, Movement, and Swipes
Touchscreen interaction involves more than just individual taps.
For example, opening an application only requires tapping its icon. But scrolling through a list or moving around a map requires handling finger movement.
That is why I implemented handling for several types of events in CaRadio: touch down, movement, and touch release.
For a regular tap, the application receives the coordinates of the point where the user touched the screen and forwards the corresponding event.
When a finger moves, the coordinate changes must be forwarded so that CarPlay can respond to the gesture.
When the finger leaves the display, a touch release event is sent.
At first glance, this seems fairly simple. But for everything to work correctly, it is important to preserve the sequence of these events and keep them separate from interactions with the head unit's own interface.
I also kept mouse support. This is useful during development and testing when no touchscreen is connected.
Screen Coordinates and CarPlay
Another task was making sure touch coordinates were transmitted correctly.
The head unit's display has its own resolution, while the CarPlay image uses a separate coordinate system.
If touchscreen coordinates are forwarded without conversion, a tap may register somewhere other than where the user intended.
That is why CaRadio recalculates the coordinates based on the application window size and the scaling of the CarPlay image.
This makes it possible to separate the physical display resolution from the coordinates used for interaction.
This approach is particularly useful during development, when the application may run in a window with dimensions different from the car head unit's display.
How to Return from CarPlay to CaRadio
When CarPlay occupies the entire screen, another question arises: how do you return to the head unit's own interface?
I could have used a separate physical button. But I wanted the main functions to be accessible directly from the touchscreen.
So I created a dedicated touch area in the upper-left corner of the display.
When the user touches this area, CaRadio handles the event itself instead of forwarding it to CarPlay as a regular tap.
This makes it possible to open the head unit's main screen and access the application's other functions.
Meanwhile, the rest of the display remains available for controlling CarPlay.
In this way, the same input device serves two different purposes: controlling the phone through CarPlay and controlling the head unit itself.
Why I Decided to Implement It Myself
My goal was not to completely rewrite FastCarPlay's controls or prove that the original implementation was incorrect.
I needed a solution that matched the specific design of CaRadio.
I wanted direct control over event handling, to understand where each touch event was being sent, and to be able to add my own interface controls.
This approach also fits my general development philosophy: whenever possible, use simple, understandable solutions without unnecessary software layers.
Of course, implementing something yourself also takes time to debug.
But the result was a control system that I could continue developing alongside the head unit's other features, without being limited by the capabilities of the original interface.
A Few Words About FastCarPlay
I also want to take a moment to thank the author of FastCarPlay.
It was thanks to his work that I was able to return to the idea of building my own CarPlay head unit and bring it to the point where I actually use it in my car.
Without this project, I might still be searching for a suitable solution or might have abandoned the idea altogether.
So the changes described here are not an attempt to make FastCarPlay "more correct" or "better." I simply adapted an existing implementation to my own hardware and requirements.
This is exactly what makes open-source projects valuable: they allow us to learn from existing solutions and develop them in our own direction instead of starting from scratch every time.
The Result
As a result, touchscreen control became part of CaRadio itself, rather than just an additional way to interact with CarPlay.
The application can now handle taps and finger movement, map coordinates to the CarPlay image, and separate phone controls from the head unit's own interface.
For me, this was another step from experimentally running CarPlay on Raspberry Pi toward a system that is genuinely convenient to use in a car.
But video and controls are only part of a multimedia system.
Audio became a separate challenge: receiving it, playing it back, and integrating it with the head unit's other functions.
I'll cover that in the next article.