Compare commits

...

147 Commits

Author SHA1 Message Date
Chris Michael efbb4d612f Comment out drm/gbm specific code (for now), while implementing Buffer
Abstraction stuff.

Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:24 +00:00
Chris Michael dd93f55773 Intialize buffer manager on engine creation
Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:24 +00:00
Chris Michael c2e190a8e2 Add function declarations for buffer manager functions
Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:24 +00:00
Chris Michael 49b27eb1c7 Add buffer manager to evas engine build order
Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:24 +00:00
Chris Michael c7adbc9292 Call idle_timeout_update when we are done processing updates.
Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:24 +00:00
Chris Michael 826d6a30f4 Add start of buffer abstraction code
Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:24 +00:00
Chris Michael 180810c009 Check return values of drm commands and fail nicely.
Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:24 +00:00
Chris Michael 219154c1e6 Add some debugging print outs
Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:24 +00:00
Chris Michael 0ee3e3f2cf Trap eglMakeCurrent return value.
For testing, fill an output with some color.
When we repaint an output, loop the sprites and signal for vblank.

Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:24 +00:00
Chris Michael 7cb302f24f Add API function to determine if sprites are supported on an output.
Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:23 +00:00
Chris Michael fe303288f9 Add function declaration for returning a fb from a bo
Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:23 +00:00
Chris Michael e5825f2d41 When we mmap the fb memory, map for read And write.
Add function to return a fb object from a bo.

Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:23 +00:00
Chris Michael c09abfc763 Add API function to determine if a crtc supports sprites
Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:23 +00:00
Chris Michael 345842da5d Fix typos
Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:23 +00:00
Chris Michael a26e161c5d Fix swapper_setup functions to pass in the fd.
Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:23 +00:00
Chris Michael 642ea4b29b Fix function prototypes to accept the drm device fd.
Fix function calls to accept the drm device fd.

Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:23 +00:00
Chris Michael 9eb1a71689 Add the 'fd' to the engine info structure
Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:23 +00:00
Chris Michael 8a065c97ac Add fd to outbuf structure, and fix swapbuf_setup function to accept
the fd.

Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:23 +00:00
Chris Michael a0691e0445 Get the fd from the ecore_drm_device so we can pass it to the evas
engine

Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:23 +00:00
Chris Michael 6f65d5c983 Add function to return the file descriptor for a ecore_drm_device
Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:23 +00:00
Chris Michael 124ce1fcfe Add API function to get the device fd.
Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:23 +00:00
Chris Michael fb2e5c2ea1 Add egl to configure.ac checks for ecore_drm
Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:23 +00:00
Chris Michael 80534ca65a Add evas engine files (for drm) to build order
Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:23 +00:00
Chris Michael f8148865c2 Modify evas_check_engine macros (for drm) to detect egl
Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:23 +00:00
Chris Michael 4c7df8ba31 Remove old outbuf file
Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:23 +00:00
Chris Michael 3cb2978f3e Add functions to show & render the ecore_evas for drm
Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:23 +00:00
Chris Michael 1984303de1 Begin work on getting evas drm engine to actually render
Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:23 +00:00
Chris Michael a7376722f7 Add API functions for returning the gbm device
Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:23 +00:00
Chris Michael 0954fca98b Add code to create an EGL Context for an output.
Start on hardware rendering code (egl)

Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:23 +00:00
Chris Michael c50fbc1e2c Add code to initialize EGL (for hardware acceleration).
Add code to repaint outputs on page_flip or vblank events.

Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:23 +00:00
Chris Michael 43bdee1489 Expose ecore_drm_fb functions as API functions
Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:23 +00:00
Chris Michael 261af2c931 Expose the Ecore_Drm_Fb structure.
Add EAPI functions for getting the gbm device, repainting outputs, and
creating/destroying framebuffer objects

Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:23 +00:00
Chris Michael e5fd334a2d Add EGL headers & necessary fields to structures for hardware
acceleration

Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:23 +00:00
Chris Michael a36cd37783 Remove libinput dependency
Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:23 +00:00
Chris Michael 084fef467b Create sprites on canvas creation
Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:23 +00:00
Chris Michael 87f9dcbb9e Fix formatting
Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:23 +00:00
Chris Michael c7b520cd80 Fix evdev code:
- Add code to setup devices and configure them
 - Add code to process input events (key down, mouse, etc).
 - Add code to create new Ecore_Drm_Evdev's

Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:23 +00:00
Chris Michael 49f0d2790b Rework input code to to actually work now:
- Remove libinput dependency.
 - Fixes input opening to not require root access anymore
 - Add code to handle inputs getting added/removed
 - Setup udev monitoring for input changes & events

Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:23 +00:00
Chris Michael d2113986f1 Remove some debug messages
Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:23 +00:00
Chris Michael 8b7c00439c Add needed fields to input & evdev structures to process input events.
Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:22 +00:00
Chris Michael d5fdaf9a73 Add Evdev device capabilities, seat capabilitities, and event type enums
Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:22 +00:00
Chris Michael f14bf0a943 Add functions to disable/enable an output
Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:22 +00:00
Chris Michael ba3c667f4a Add function to disable inputs
When we enable inputs, don't recreate the fd handler if we do not need
to

Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:22 +00:00
Chris Michael 624fe65bda Enable/Disable outputs & inputs during vt switch
Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:22 +00:00
Chris Michael b30dfbe4c2 Add TODO
Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:22 +00:00
Chris Michael 73a7ac2ec8 Fix Mesa bug with some drivers by preloading libglapi
Some mesa drivers (i965, etc) require glapi functions but a current
Mesa bug does not always link those drivers to glapi so we will
preload the library to avoid "driver loading" errors.

Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:22 +00:00
Chris Michael 5d008992d1 Add sprites file
Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:22 +00:00
Chris Michael 4e6fd8fee9 Add Ecore_Drm_Sprite structure
Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:22 +00:00
Chris Michael 8f40229dc2 Add additional API functions:
- Add functions for enabling an output and setting cursor size
 - Add functions for disabling an input
 - Add functions for creating/destroying sprites

Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:22 +00:00
Chris Michael 44333dda7d Add sprites file to build order
Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:22 +00:00
Chris Michael 9584b01c03 Add ecore_drm_fb file for framebuffer functions
Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:22 +00:00
Chris Michael 0f0eb80fc5 Add code to setup drm outputs for hardware or software rendering
Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:22 +00:00
Chris Michael 6a425e6a3a Add start of drm hw acceleration support
Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:22 +00:00
Chris Michael 1bba4b85e8 Add Ecore_Drm_Fb structure and function declarations
Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:22 +00:00
Chris Michael 26244208ef Add fb file to drm build order
Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:22 +00:00
Chris Michael 6571f552c4 Fix configure.ac for drm hw acceleration
Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:22 +00:00
Chris Michael 0e1a9bd077 Create outputs & inputs in the ecore_evas code
Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:22 +00:00
Chris Michael 94f2c999ca Cleanup slave process code:
- Remove unused credentials code
 - Add a Device_Close operation

Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:22 +00:00
Chris Michael 4af3cd4099 Remove unused Credentials code & remove debugging comments.
Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:22 +00:00
Chris Michael 56efb6ae64 Cleanup drm device code and add a handler for page_flip & vblank events.
- We don't need the slave process to open the drm device anymore.

Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:22 +00:00
Chris Michael 89c3d3079a Cleanup tty code:
- We don't need the slave process to open the tty anymore.

Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:22 +00:00
Chris Michael af2fdf7cd7 Add output, input, and evdev structures.
Cleanup header includes.
Add private function prototypes for evdev functions

Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:22 +00:00
Chris Michael dd20bcffa7 Add structures and API function declarations for outputs & inputs.
Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:22 +00:00
Chris Michael 74445104d0 Add output, input, and evdev files to build order
Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:22 +00:00
Chris Michael 57fd12a75d Add output, input, and evdev files
Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:22 +00:00
Chris Michael 0872c9b1f5 Add call to close the tty when we shutdown.
Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:22 +00:00
Chris Michael 473156ab76 Fix makefile for ecore_drm setuid binary.
Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:22 +00:00
Chris Michael ce667f0c42 Add setuid functions for drm master drop & set.
Add case(s) for master drop/set commands.

Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:22 +00:00
Chris Michael b51b92522f Rework message send/receive functions for new prototypes.
Add case(s) for drm master drop/set commands.

Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:22 +00:00
Chris Michael 874609a680 Rework message send/receive functions to match new prototypes.
Send message to binary for drm_master_drop/set functions.

Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:22 +00:00
Chris Michael 5b06621c05 Catch VT Switching signals on the tty and drop/set drm master.
Comment out turning off keyboard (for now).
Adjust message send/receive functions for new prototypes.
Add tty release/acquire functions.

Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:22 +00:00
Chris Michael 45c5605840 Rework message send/receive functions to be able to set an fd easier.
Add drm clock monotonic define if it does not exist.

Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:22 +00:00
Chris Michael d1da3d85f7 Add operations for master drop & set.
Add functions for tty release & acquire

Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:22 +00:00
Chris Michael 523b61413e Add functionc all to open a tty
Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:22 +00:00
Chris Michael cc141cfd92 Fix up slave process to properly send/recv messages now
NB: Temporary commit so I can continue from home

Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:21 +00:00
Chris Michael 694e685bf5 Fix up drm message passing to properly function and not leak memory.
NB: Just a temporary git push so I can continue work from home.

Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:21 +00:00
Chris Michael 672413a97e Add headers for ioctl and vt functions
Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:21 +00:00
Chris Michael 845994c441 Add code to open a drm device
Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:21 +00:00
Chris Michael 6c6aef928c Don't need epoll fd as static
Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:21 +00:00
Chris Michael 09cbfb0e36 Don't pass MSG_NOSIGNAL to send as we Want to know about errors
immediately.

Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:21 +00:00
Chris Michael edf9eee500 Rework ecore_drm slave code:
- This reworks setuid slave process to pass the fd's credentials to
the parent process.
  - Use epoll instead of normal poll() .. (faster)

Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:21 +00:00
Chris Michael 661177022a Rework ecore_drm code:
- This rewrite is so we can support passing credentials along with fds.
  - When we receive an 'fd' message from the slave process, we can now
have code which also gets the credentials (code incomplete still).

Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:21 +00:00
Chris Michael 521260b916 Fix compile error with variable typo
Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:21 +00:00
Chris Michael 128a355f17 Comment out sigusr handler (for now)
Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:21 +00:00
Chris Michael e8ff6be5ca Add missing includes for drm functions
Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:21 +00:00
Chris Michael 54c4a4675d Add function code for functions:
ecore_drm_device_close, ecore_drm_device_master_get,
ecore_drm_device_master_set, ecore_drm_device_master_drop

This functions will be used for drm rendering to set the drm device as
master, or to drop being master, etc, etc.

Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:21 +00:00
Chris Michael 3b79d6841b Add API function prototypes for drm device master_get/set/drop functions
Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:21 +00:00
Chris Michael 11a6485f70 Cleanup ecore_evas_drm creation code
Move ecore_drm function calls into separate "init" function for
readability.

Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:21 +00:00
Chris Michael 4a242ac724 Add code to setup tty when it is opened.
Add function to close an FD.

After we open a tty, we need to send some ioctl()s to put it into
graphics mode. Also, add code to actually close an FD. This can close
a tty, drm device fd, etc, etc.

Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:21 +00:00
Chris Michael 6b36a36179 Setup tty event handler and add code to close a tty.
After the tty is opened, we need to listen for USR1 & USR2 signals
being sent from the kernel so that we can acquire or release a vt

Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:21 +00:00
Chris Michael fc38fc173c Make slave spawning function return boolean
We should test that spawning the launcher slave has worked, so make
the function return a boolean that we can test with

Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:21 +00:00
Chris Michael 1e1b9d77de Remove ioctl headers and comment out drm headers
ioctl() and drm function calls will be made from the slave process, so
there is no need to include them here.

Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:21 +00:00
Chris Michael 314a2364c6 Don't need to print error messages unless there is an error
Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:21 +00:00
Chris Michael 8bcc51addf Add call to close the tty if something fails
Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:21 +00:00
Chris Michael 2d7a7da291 When we try to open a tty, check that it is an actual & valid tty
Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:21 +00:00
Chris Michael 27a61cd4d0 Cleanup tty_open function
Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:21 +00:00
Chris Michael e4c1a1fc33 Remove redundant variable (we don't need 'fd' here).
Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:21 +00:00
Chris Michael 77f023268d Remove debug print message
Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:21 +00:00
Chris Michael e2dfb240f6 Add function call to open default tty
Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:21 +00:00
Chris Michael b2590044e9 Add start of tty code for open/close/etc tty operations
Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:21 +00:00
Chris Michael 2f78806319 Add ecore_drm_tty file to build order
Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:21 +00:00
Chris Michael c537913e4d Add tty fields to Ecore_Drm_Device structure
Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:21 +00:00
Chris Michael 492f6bc150 Add operation for opening tty
This adds new enum values to Operation, and function prototypes for
tty open/code functions

Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:21 +00:00
Chris Michael e84800c8cb Add start of TTY_OPEN operation code
Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:21 +00:00
Chris Michael cf2aa071cf Fix fd passing bugger/compiler warning
Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:21 +00:00
Chris Michael fb0d304f14 Add code to open a drm device and send the resulting operation status
& fd back to parent process.

Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:20 +00:00
Chris Michael 1e19eac751 Get result of call to ecore_drm_device_open
Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:20 +00:00
Chris Michael e0bdbd62c1 Add function to receive operation result messages.
Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:20 +00:00
Chris Michael 43911a80fe Add private function prototype for receiving operation status messages.
Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:20 +00:00
Chris Michael 405b0865a4 Fix operation enum when opening device
Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:20 +00:00
Chris Michael 055669d5c7 Rename drm operation enum members
We will eventually have a separate enum value for opening inputs vs
opening actual devices, so change enum now

Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:20 +00:00
Chris Michael 79e42237f0 Remove compiler warning
Copy the device name (/dev/dri/card*) into a buffer so we can pass it
to the send function.

Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:20 +00:00
Chris Michael 3058e2f6bc Fix error message
Ecore_Drm_Device is an opaque structure (currently), so we cannot
print the device name here.

Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:20 +00:00
Chris Michael 4965f01088 Fix receiving messages from ecore_drm library
Previously, messages were not being sent/received properly
(incomplete). Ecore_Drm library will now send us messages in 2 parts
(opcode & data) so we need to change the message receiving code to
reflect that.

Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:20 +00:00
Chris Michael 68eb4ba837 Add function call to open a drm device. Store device id.
Calls to ecore_drm_device_open will now send a message to the slave
process to open the drm device.

Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:20 +00:00
Chris Michael b12bdc779b Fix sending of messages to ecore_drm_launch slave
Previously, sending messages & fds did not quite work right...messages
were not being fully sent.

This fixes the problem by breaking the Ecore_Drm_Message into 2 parts
(iovs). Now we will send the opcode as one iov, and the data as another.

Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:20 +00:00
Chris Michael bf2d1cc4b3 Add private function prototype for sending messages
Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:20 +00:00
Chris Michael 56bcf2f107 Add 'id' field to Ecore_Drm_Device
Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:20 +00:00
Chris Michael 2ec85c631e Add function call to try and open the drm device
Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:20 +00:00
Chris Michael 47e3268628 Add functions to open and close a drm device
This just adds skeletons for those API functions. Actual code to
follow soon ;)

Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:20 +00:00
Chris Michael 461ffcd83d Add device path to Ecore_Drm_Device structure
Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:20 +00:00
Chris Michael d3dbfc27c8 Add API functions for device open & device close
Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:20 +00:00
Chris Michael 04bdebcd04 Fix typo of incorrect device field
Ecore_Drm_Device has field 'devname' so that's the one we need to
cleanup

Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:20 +00:00
Chris Michael 92fa2ed483 Start on code for ecore_evas_drm to find the graphics device
This adds a function call into ecore_drm library to discover the
default graphics card to use for drm.

Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:20 +00:00
Chris Michael 35580e3378 Add udev support to ecore_drm library. Also close the open sockets on
error.

This adds udev support for graphics card device enumeration. Udev will
also be used in ecore_drm for "other" devices too....

Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:20 +00:00
Chris Michael 39f15e6142 Add ecore_drm_device file for device-specific functions
This adds the start of an ecore_drm_device file with functions that
will pertain to graphics devices (opening, closing, etc).

Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:20 +00:00
Chris Michael 3b57f1ddcd Add udev header and start to define Ecore_Drm_Device structure.
Udev header is needed so we can do drm device discovery (among other
devices).

NB: The structure for Ecore_Drm_Device is NOT exposed to users of the
API currently. It may stay opaque....unclear yet. This structure is
NOT finalized !!! Do not try to use it !!

Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:20 +00:00
Chris Michael b41d7e67af Rework device API a bit and add a device_free function
This change adds a 'seat' parameter to the ecore_drm_device_find
function for located devices on a specific seat. This also adds a new
API function for freeing any returned Ecor_Drm_Device structure

Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:20 +00:00
Chris Michael 056fb866e9 Include Eina.h header so we can make use of EINA_UNUSED
Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:20 +00:00
Chris Michael 099e17f58a Add ecore_drm_device to build order
Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:20 +00:00
Chris Michael 6e14f2c317 Remove unused variable
Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:20 +00:00
Chris Michael 3459a23b09 Add code to start slave process, and code to send messages to it
This adds the ability for ecore_drm to launch the
ecore_drm slave process (for privileged device access), and to send
messages from ecore_drm library to the slave process.

Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:20 +00:00
Chris Michael cda78b1b12 Add additional internal headers we need for various functions
Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:20 +00:00
Chris Michael e4d203762a Add operation support to ecore_drm (types and enums for message passing)
This adds a structure to be used for passing messages between
ecore_drm library and the ecore_drm_launch slave process. Also added
here is an enum for Operations, and an enum for Operation Results.

Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:20 +00:00
Chris Michael 0be06f44b3 Add ability for ecore_drm_launch slave process to receive messages
This adds the ability for ecore_drm_launch to accept and act on any
messages sent from ecore_drm library. This includes the ability to
pass file descriptors from ecore_drm library to the slave process for
opening/closing/etc.

Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:20 +00:00
Chris Michael 68d94c340e Fix Ecore_Drm Makefile to install slave process
This fixes the Ecore_Drm Makefile to install the ecore_drm_launch
slave process into a directory using MODULE_ARCH

Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:20 +00:00
Chris Michael 70b603572b Add DRM to list of available engines
Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:20 +00:00
Chris Michael 702e3d42eb Add code to ecore_evas drm engine to initialize ecore_drm and setup
the canvas.

Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:20 +00:00
Chris Michael df98346938 Add start of ecore_drm_launch code
Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:20 +00:00
Chris Michael 6475a69c29 Add start of ecore_drm code
Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:20 +00:00
Chris Michael c47017aa91 Add ecore_drm_private file
Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:20 +00:00
Chris Michael e48ff7c3f3 Add Ecore_Drm header file
Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:19 +00:00
Chris Michael 286ceeed13 Add Ecore_Drm pkgconfig file
Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:19 +00:00
Chris Michael 55c5e96405 Add Makefile_Ecore_Drm.am to src/Makefile.am
Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:19 +00:00
Chris Michael 57fe700547 Add Makefile for Ecore_Drm
Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:19 +00:00
Chris Michael 56844d19a3 Fix Makefile.am to add evas-drm.pc and ecore-drm.pc files
Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:19 +00:00
Chris Michael 3eea4099e4 Add configure.ac autofoo for building ecore-drm
Signed-off-by: Chris Michael <cp.michael@samsung.com>
2014-01-29 15:27:19 +00:00
27 changed files with 5830 additions and 163 deletions

View File

@ -187,10 +187,18 @@ if BUILD_ENGINE_WAYLAND_EGL
pkgconfig_DATA += pc/evas-wayland-egl.pc
endif
if BUILD_ENGINE_DRM
pkgconfig_DATA += pc/evas-drm.pc
endif
if HAVE_ECORE_COCOA
pkgconfig_DATA += pc/ecore-cocoa.pc
endif
if HAVE_ECORE_DRM
pkgconfig_DATA += pc/ecore-drm.pc
endif
if HAVE_ECORE_FB
pkgconfig_DATA += pc/ecore-fb.pc
endif

View File

@ -1107,6 +1107,17 @@ AC_ARG_ENABLE([drm],
],
[want_drm="no"])
AC_ARG_ENABLE([drm_hw_accel],
[AC_HELP_STRING([--enable-drm-hw-accel],
[enable drm hardware acceleration. @<:@default=disabled@:>@])],
[
if test "x${enableval}" = "xyes" ; then
want_drm_hw_accel="yes"
else
want_drm_hw_accel="no"
fi
],
[want_drm_hw_accel="no"])
# Fontconfig
AC_ARG_ENABLE([fontconfig],
@ -2476,6 +2487,46 @@ EFL_LIB_END_OPTIONAL([Ecore_Wayland])
#### End of Ecore_Wayland
#### Ecore_Drm
EFL_LIB_START_OPTIONAL([Ecore_Drm], [test "${want_drm}" = "yes"])
### Additional options to configure
SUID_CFLAGS=-fPIE
SUID_LDFLAGS=-pie
AC_SUBST([SUID_CFLAGS])
AC_SUBST([SUID_LDFLAGS])
### Default values
### Checks for programs
### Checks for libraries
EFL_INTERNAL_DEPEND_PKG([ECORE_DRM], [ecore])
EFL_INTERNAL_DEPEND_PKG([ECORE_DRM], [ecore-input])
EFL_INTERNAL_DEPEND_PKG([ECORE_DRM], [eo])
EFL_INTERNAL_DEPEND_PKG([ECORE_DRM], [eina])
EFL_DEPEND_PKG([ECORE_DRM], [DRM], [libudev >= 148 libdrm >= 2.4])
EFL_OPTIONAL_DEPEND_PKG([ECORE_DRM], [${want_drm_hw_accel}], [GBM], [gbm egl >= 7.10 glesv2])
EFL_EVAL_PKGS([ECORE_DRM])
### Checks for header files
### Checks for types
### Checks for structures
### Checks for compiler characteristics
### Checks for linker characteristics
### Checks for library functions
EFL_LIB_END_OPTIONAL([Ecore_Drm])
AM_CONDITIONAL([BUILD_ECORE_DRM_HW_ACCEL], [test "x${want_drm_hw_accel}" = "xyes"])
#### End of Ecore_Drm
#### Ecore_Audio
AC_ARG_ENABLE([audio],
@ -3298,7 +3349,6 @@ AM_CONDITIONAL([BUILD_ECORE_EVAS_SDL],
ECORE_EVAS_MODULE([wayland-shm], [${want_wayland}])
ECORE_EVAS_MODULE([wayland-egl], [${want_ecore_evas_wayland_egl}])
ECORE_EVAS_MODULE([drm], [${want_drm}])
build_ecore_evas_wayland="no"
if test "x${have_ecore_evas_wayland_shm}" = "xyes" || \
@ -3387,6 +3437,10 @@ if test "x$have_ecore_evas_software_x11" = "xyes" || \
fi
AM_CONDITIONAL([BUILD_ECORE_EVAS_X11], [test "${build_ecore_evas_x11}" = "yes"])
ECORE_EVAS_MODULE([drm], [${want_drm}],
[ EFL_INTERNAL_DEPEND_PKG([ECORE_EVAS], [ecore-drm]) ]
)
EFL_EVAL_PKGS([ECORE_EVAS])
### Checks for header files
@ -3918,6 +3972,7 @@ pc/ecore-file.pc
pc/ecore-input.pc
pc/ecore-input-evas.pc
pc/ecore-cocoa.pc
pc/ecore-drm.pc
pc/ecore-fb.pc
pc/ecore-psl1ght.pc
pc/ecore-sdl.pc

View File

@ -591,10 +591,18 @@ AC_DEFUN([EVAS_CHECK_ENGINE_DEP_DRM],
[
requirement=""
requirement_egl=""
have_dep="no"
have_egl="no"
evas_engine_[]$1[]_cflags=""
evas_engine_[]$1[]_libs=""
if test "x${with_opengl}" = "xes" ; then
gl_library="glesv2"
else
gl_library="no"
fi
PKG_CHECK_EXISTS([libdrm],
[
have_dep="yes"
@ -602,12 +610,22 @@ PKG_CHECK_EXISTS([libdrm],
],
[have_dep="no"])
if test "x${gl_library}" = "xglesv2" ; then
PKG_CHECK_EXISTS([egl >= 7.10 ${gl_library}],
[
have_egl="yes"
requirement_egl="egl >= 7.10 ${gl_library}"
],
[have_egl="no"])
fi
if test "x${have_dep}" = "xyes" ; then
if test "x$3" = "xstatic" ; then
requirements_pc_evas="${requirement} ${requirements_pc_evas}"
requirements_pc_deps_evas="${requirement} ${requirements_pc_deps_evas}"
requirements_pc_evas="${requirement} ${requirement_egl} ${requirements_pc_evas}"
requirements_pc_deps_evas="${requirement} ${requirement_egl} ${requirements_pc_deps_evas}"
else
PKG_CHECK_MODULES([DRM], [${requirement}])
PKG_CHECK_MODULES([DRM], [${requirement} ${requirement_egl}])
evas_engine_[]$1[]_cflags="${DRM_CFLAGS}"
evas_engine_[]$1[]_libs="${DRM_LIBS}"
fi

12
pc/ecore-drm.pc.in Normal file
View File

@ -0,0 +1,12 @@
prefix=@prefix@
exec_prefix=@exec_prefix@
libdir=@libdir@
includedir=@includedir@
Name: ecore-drm
Description: E core library, DRM module
Requires.private: @requirements_pc_ecore_drm@
Version: @VERSION@
Libs: -L${libdir} -lecore_drm
Libs.private: @requirements_libs_ecore_drm@
Cflags: -I${includedir}/efl-@VMAJ@ -I${includedir}/ecore-drm-@VMAJ@

View File

@ -33,6 +33,7 @@ include Makefile_Ecore_File.am
include Makefile_Ecore_Input.am
include Makefile_Ecore_Input_Evas.am
include Makefile_Ecore_Cocoa.am
include Makefile_Ecore_Drm.am
include Makefile_Ecore_FB.am
include Makefile_Ecore_Psl1ght.am
include Makefile_Ecore_SDL.am

56
src/Makefile_Ecore_Drm.am Normal file
View File

@ -0,0 +1,56 @@
if HAVE_ECORE_DRM
### Library
lib_LTLIBRARIES += lib/ecore_drm/libecore_drm.la
installed_ecoredrmmainheadersdir = $(includedir)/ecore-drm-@VMAJ@
dist_installed_ecoredrmmainheaders_DATA = \
lib/ecore_drm/Ecore_Drm.h
lib_ecore_drm_libecore_drm_la_SOURCES = \
lib/ecore_drm/ecore_drm_sprites.c \
lib/ecore_drm/ecore_drm_fb.c \
lib/ecore_drm/ecore_drm_evdev.c \
lib/ecore_drm/ecore_drm_inputs.c \
lib/ecore_drm/ecore_drm_output.c \
lib/ecore_drm/ecore_drm_tty.c \
lib/ecore_drm/ecore_drm_device.c \
lib/ecore_drm/ecore_drm.c \
lib/ecore_drm/ecore_drm_private.h
lib_ecore_drm_libecore_drm_la_CPPFLAGS = \
-I$(top_builddir)/src/lib/efl \
@ECORE_DRM_CFLAGS@ \
-DPACKAGE_LIB_DIR=\"$(libdir)\" \
-DMODULE_ARCH=\"$(MODULE_ARCH)\"
lib_ecore_drm_libecore_drm_la_LIBADD = @ECORE_DRM_LIBS@
lib_ecore_drm_libecore_drm_la_DEPENDENCIES = @ECORE_DRM_INTERNAL_LIBS@
lib_ecore_drm_libecore_drm_la_LDFLAGS = @EFL_LTLIBRARY_FLAGS@
### Drm Launch Binary
ecoredrmlaunchinternal_bindir = $(libdir)/ecore_drm/bin/$(MODULE_ARCH)
ecoredrmlaunchinternal_bin_PROGRAMS = bin/ecore_drm/ecore_drm_launch
bin_ecore_drm_ecore_drm_launch_SOURCES = \
bin/ecore_drm/ecore_drm_launch.c
bin_ecore_drm_ecore_drm_launch_CPPFLAGS = \
-I$(top_builddir)/src/lib/efl \
@ECORE_DRM_CFLAGS@ @SUID_CFLAGS@ \
-DPACKAGE_LIB_DIR=\"$(libdir)\" \
-DMODULE_ARCH=\"$(MODULE_ARCH)\"
bin_ecore_drm_ecore_drm_launch_LDADD = \
@ECORE_DRM_LIBS@ \
@SUID_LDFLAGS@
bin_ecore_drm_ecore_drm_launch_DEPENDENCIES = \
@USE_ECORE_DRM_INTERNAL_LIBS@
setuid_root_mode = a=rx,u+xs
EFL_INSTALL_EXEC_HOOK += @chmod $(setuid_root_mode) $(DESTDIR)$(libdir)/ecore_drm/bin/$(MODULE_ARCH)/ecore_drm_launch$(EXEEXT) || true;
endif

View File

@ -961,9 +961,11 @@ endif
if BUILD_ENGINE_DRM
dist_installed_evasmainheaders_DATA += modules/evas/engines/drm/Evas_Engine_Drm.h
DRM_SOURCES = \
modules/evas/engines/drm/evas_outbuf.c \
modules/evas/engines/drm/evas_engine.c \
modules/evas/engines/drm/evas_engine.h
modules/evas/engines/drm/evas_engine.h \
modules/evas/engines/drm/evas_swapbuf.c \
modules/evas/engines/drm/evas_swapper.c \
modules/evas/engines/drm/evas_buffer_manager.c
if EVAS_STATIC_BUILD_DRM
lib_evas_libevas_la_SOURCES += $(DRM_SOURCES)
lib_evas_libevas_la_CPPFLAGS += @evas_engine_drm_cflags@

View File

@ -0,0 +1,407 @@
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
/* standard headers */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <limits.h>
#include <fcntl.h>
#include <errno.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <sys/ioctl.h>
/* #include <syslog.h> */
/* #include <pwd.h> */
/* #include <linux/major.h> */
/* #include <linux/vt.h> */
/* #include <linux/kd.h> */
#include <xf86drm.h>
#include <xf86drmMode.h>
#include <drm_fourcc.h>
#include <Eina.h>
#include <Ecore_Drm.h>
#define RIGHTS_LEN CMSG_LEN(sizeof(int))
#define IOVSET(_iov, _addr, _len) \
(_iov)->iov_base = (void *)(_addr); \
(_iov)->iov_len = (_len);
/* local prototypes */
static int _send_msg(int opcode, int fd, void *data, size_t bytes);
/* local variables */
static struct cmsghdr *cmsgptr = NULL;
static int _read_fd = -1;
static int _write_fd = -1;
static int
_open_device(const char *device)
{
int fd = -1, ret = ECORE_DRM_OP_SUCCESS;
if (!device)
{
ret = ECORE_DRM_OP_FAILURE;
_send_msg(ECORE_DRM_OP_DEVICE_OPEN, fd, &ret, sizeof(int));
return ret;
}
fprintf(stderr, "Launcher Trying to Open Device: %s\n", device);
if ((fd = open(device, O_RDWR | O_NONBLOCK)) < 0)
{
fprintf(stderr, "Failed to Open Device: %s: %m\n", device);
ret = ECORE_DRM_OP_FAILURE;
}
else
fprintf(stderr, "Launcher Opened Device: %s %d\n", device, fd);
_send_msg(ECORE_DRM_OP_DEVICE_OPEN, fd, &ret, sizeof(int));
return ret;
}
static int
_close_device(int fd)
{
int ret = ECORE_DRM_OP_SUCCESS;
if (!fd)
{
ret = ECORE_DRM_OP_FAILURE;
_send_msg(ECORE_DRM_OP_DEVICE_CLOSE, fd, &ret, sizeof(int));
return ret;
}
close(fd);
_send_msg(ECORE_DRM_OP_DEVICE_CLOSE, fd, &ret, sizeof(int));
return ret;
}
static int
_open_tty(const char *name)
{
int fd = -1, ret = ECORE_DRM_OP_SUCCESS;
/* struct stat st; */
if (!name) goto fail;
fprintf(stderr, "Launcher Trying to Open Tty: %s\n", name);
if ((fd = open(name, O_RDWR | O_NOCTTY)) < 0)
{
fprintf(stderr, "Failed to Open Tty: %s: %m\n", name);
goto fail;
}
else
fprintf(stderr, "Launcher Opened Tty: %s %d\n", name, fd);
/* if ((fstat(fd, &st) == -1) || */
/* (major(st.st_rdev) != TTY_MAJOR) || (minor(st.st_rdev) == 0)) */
/* { */
/* fprintf(stderr, "%d is Not a Tty\n", fd); */
/* goto fail; */
/* } */
_send_msg(ECORE_DRM_OP_TTY_OPEN, fd, &ret, sizeof(int));
return ret;
fail:
if (fd > -1) close(fd);
fd = -1;
ret = ECORE_DRM_OP_FAILURE;
_send_msg(ECORE_DRM_OP_DEVICE_OPEN, fd, &ret, sizeof(int));
return ret;
}
static int
_drop_master(int fd)
{
int ret = ECORE_DRM_OP_SUCCESS;
fprintf(stderr, "Drop Master: %d\n", fd);
if (drmDropMaster(fd) != 0)
{
ret = ECORE_DRM_OP_FAILURE;
fprintf(stderr, "\tFailed to drop master: %m\n");
}
_send_msg(ECORE_DRM_OP_DEVICE_MASTER_DROP, fd, &ret, sizeof(int));
close(fd);
return ret;
}
static int
_set_master(int fd)
{
int ret = ECORE_DRM_OP_SUCCESS;
fprintf(stderr, "Set Master: %d\n", fd);
if (drmSetMaster(fd) != 0)
{
ret = ECORE_DRM_OP_FAILURE;
fprintf(stderr, "\tFailed to set master: %m\n");
}
_send_msg(ECORE_DRM_OP_DEVICE_MASTER_SET, fd, &ret, sizeof(int));
close(fd);
return ret;
}
static int
_read_fd_get(void)
{
char *ev, *end;
int fd = -1, flags = -1;
if (!(ev = getenv("ECORE_DRM_LAUNCHER_SOCKET_READ")))
return -1;
fd = strtol(ev, &end, 0);
if (*end != '\0') return -1;
flags = fcntl(fd, F_GETFD);
if (flags < 0) return -1;
fprintf(stderr, "Got Read FD: %d\n", fd);
return fd;
}
static int
_write_fd_get(void)
{
char *ev, *end;
int fd = -1, flags = -1;
if (!(ev = getenv("ECORE_DRM_LAUNCHER_SOCKET_WRITE")))
return -1;
fd = strtol(ev, &end, 0);
if (*end != '\0') return -1;
flags = fcntl(fd, F_GETFD);
if (flags < 0) return -1;
fprintf(stderr, "Got Write FD: %d\n", fd);
return fd;
}
static int
_send_msg(int opcode, int fd, void *data, size_t bytes)
{
Ecore_Drm_Message dmsg;
struct iovec iov[2];
struct msghdr msg;
ssize_t size;
/* send a message to the calling process */
/* 'fd' is the fd to send */
memset(&dmsg, 0, sizeof(dmsg));
IOVSET(iov + 0, &dmsg, sizeof(dmsg));
IOVSET(iov + 1, &data, bytes);
dmsg.opcode = opcode;
dmsg.size = bytes;
msg.msg_name = NULL;
msg.msg_namelen = 0;
msg.msg_iov = iov;
msg.msg_iovlen = 2;
msg.msg_flags = 0;
if ((!cmsgptr) && (!(cmsgptr = malloc(RIGHTS_LEN))))
return -1;
cmsgptr->cmsg_level = SOL_SOCKET;
cmsgptr->cmsg_type = SCM_RIGHTS;
cmsgptr->cmsg_len = RIGHTS_LEN;
msg.msg_control = cmsgptr;
msg.msg_controllen = RIGHTS_LEN;
fprintf(stderr, "Launcher Sending FD: %d\n", fd);
*((int *)CMSG_DATA(cmsgptr)) = fd;
errno = 0;
size = sendmsg(_write_fd, &msg, MSG_EOR);
if (errno != 0)
{
fprintf(stderr, "Failed to send message: %s", strerror(errno));
return -1;
}
fprintf(stderr, "Launcher Wrote %li to %d\n", size, _write_fd);
return size;
}
static int
_recv_msg(void)
{
int fd = -1;
Ecore_Drm_Message dmsg;
struct iovec iov[2];
struct msghdr msg;
struct cmsghdr *cmsg = NULL;
char data[BUFSIZ];
ssize_t size;
fprintf(stderr, "Received Message\n");
memset(&dmsg, 0, sizeof(dmsg));
memset(&data, 0, sizeof(data));
IOVSET(iov + 0, &dmsg, sizeof(dmsg));
IOVSET(iov + 1, &data, sizeof(data));
msg.msg_name = NULL;
msg.msg_namelen = 0;
msg.msg_iov = iov;
msg.msg_iovlen = 2;
msg.msg_flags = 0;
if ((!cmsgptr) && (!(cmsgptr = malloc(RIGHTS_LEN))))
return -1;
msg.msg_control = cmsgptr;
msg.msg_controllen = RIGHTS_LEN;
errno = 0;
size = recvmsg(_read_fd, &msg, 0);
if (errno != 0)
{
fprintf(stderr, "Failed to receive message: %m\n");
return -1;
}
fprintf(stderr, "\tReceived %li bytes from %d\n", size, _read_fd);
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
cmsg = CMSG_NXTHDR(&msg, cmsg))
{
if (cmsg->cmsg_level != SOL_SOCKET)
continue;
switch (cmsg->cmsg_type)
{
case SCM_RIGHTS:
fd = *((int *)CMSG_DATA(cmsg));
switch (dmsg.opcode)
{
case ECORE_DRM_OP_DEVICE_OPEN:
fprintf(stderr, "Open Device: %s\n", (char *)data);
_open_device((char *)data);
break;
case ECORE_DRM_OP_DEVICE_CLOSE:
fprintf(stderr, "Close Device: %d\n", fd);
_close_device(fd);
case ECORE_DRM_OP_TTY_OPEN:
fprintf(stderr, "Open Tty: %s\n", (char *)data);
_open_tty((char *)data);
break;
case ECORE_DRM_OP_DEVICE_MASTER_DROP:
fprintf(stderr, "Drop Master: %d\n", fd);
_drop_master(fd);
break;
case ECORE_DRM_OP_DEVICE_MASTER_SET:
fprintf(stderr, "Set Master\n");
_set_master(fd);
break;
default:
fprintf(stderr, "Unhandled Opcode: %d\n", dmsg.opcode);
break;
}
break;
default:
fprintf(stderr, "Unhandled message type: %d\n", cmsg->cmsg_type);
return -1;
break;
}
}
return size;
}
int
main(int argc EINA_UNUSED, char **argv EINA_UNUSED)
{
struct epoll_event ev, events[1];
int ret, i, _epoll_fd = -1;
setvbuf(stdout, NULL, _IONBF, 0);
setvbuf(stderr, NULL, _IONBF, 0);
fprintf(stderr, "Spartacus Is Alive\n");
_read_fd = _read_fd_get();
if (_read_fd < 0) return EXIT_FAILURE;
_write_fd = _write_fd_get();
if (_write_fd < 0) return EXIT_FAILURE;
fprintf(stderr, "Creating Epoll\n");
_epoll_fd = epoll_create(1);
memset(&ev, 0, sizeof(ev));
ev.events = EPOLLIN;
ev.data.fd = _read_fd;
if (epoll_ctl(_epoll_fd, EPOLL_CTL_ADD, _read_fd, &ev) < 0)
{
return EXIT_FAILURE;
}
memset(&events, 0, sizeof(events));
while (1)
{
ret = epoll_wait(_epoll_fd, events, sizeof(events) / sizeof(struct epoll_event), -1);
if (ret < 0)
{
fprintf(stderr, "Epoll Failed: %m\n");
return EXIT_FAILURE;
}
for (i = 0; i < ret; i++)
{
fprintf(stderr, "Epoll Event on: %d\n", events[i].data.fd);
if (events[i].data.fd != _read_fd) continue;
if (events[i].events & EPOLLIN)
{
fprintf(stderr, "Epoll Data In\n");
_recv_msg();
}
else if (events[i].events & EPOLLERR)
{
fprintf(stderr, "Epoll Data Error\n");
}
}
}
fprintf(stderr, "Spartacus Is Dead\n");
return EXIT_SUCCESS;
}

View File

@ -0,0 +1,171 @@
#ifndef _ECORE_DRM_H
# define _ECORE_DRM_H
# ifdef EAPI
# undef EAPI
# endif
#ifdef _MSC_VER
# ifdef BUILDING_DLL
# define EAPI __declspec(dllexport)
# else // ifdef BUILDING_DLL
# define EAPI __declspec(dllimport)
# endif // ifdef BUILDING_DLL
#else // ifdef _MSC_VER
# ifdef __GNUC__
# if __GNUC__ >= 4
# define EAPI __attribute__ ((visibility("default")))
# else // if __GNUC__ >= 4
# define EAPI
# endif // if __GNUC__ >= 4
# else // ifdef __GNUC__
# define EAPI
# endif // ifdef __GNUC__
#endif // ifdef _MSC_VER
typedef enum _Ecore_Drm_Op
{
ECORE_DRM_OP_READ_FD_SET,
ECORE_DRM_OP_WRITE_FD_SET,
ECORE_DRM_OP_DEVICE_OPEN,
ECORE_DRM_OP_DEVICE_CLOSE,
ECORE_DRM_OP_DEVICE_MASTER_DROP,
ECORE_DRM_OP_DEVICE_MASTER_SET,
ECORE_DRM_OP_TTY_OPEN,
ECORE_DRM_OP_TTY_CLOSE
} Ecore_Drm_Op;
typedef enum _Ecore_Drm_Op_Result
{
ECORE_DRM_OP_SUCCESS,
ECORE_DRM_OP_FAILURE
} Ecore_Drm_Op_Result;
typedef enum _Ecore_Drm_Evdev_Capabilities
{
EVDEV_KEYBOARD = (1 << 0),
EVDEV_BUTTON = (1 << 1),
EVDEV_MOTION_ABS = (1 << 2),
EVDEV_MOTION_REL = (1 << 3),
EVDEV_TOUCH = (1 << 4),
} Ecore_Drm_Evdev_Capabilities;
typedef enum _Ecore_Drm_Evdev_Event_Type
{
EVDEV_NONE,
EVDEV_ABSOLUTE_TOUCH_DOWN,
EVDEV_ABSOLUTE_MOTION,
EVDEV_ABSOLUTE_TOUCH_UP,
EVDEV_ABSOLUTE_MT_DOWN,
EVDEV_ABSOLUTE_MT_MOTION,
EVDEV_ABSOLUTE_MT_UP,
EVDEV_RELATIVE_MOTION,
} Ecore_Drm_Evdev_Event_Type;
typedef enum _Ecore_Drm_Seat_Capabilities
{
EVDEV_SEAT_POINTER = (1 << 0),
EVDEV_SEAT_KEYBOARD = (1 << 1),
EVDEV_SEAT_TOUCH = (1 << 2),
} Ecore_Drm_Seat_Capabilities;
/* structure for message passing */
typedef struct _Ecore_Drm_Message
{
int opcode, size;
void *data;
} Ecore_Drm_Message;
/* structure for fb objects */
typedef struct _Ecore_Drm_Fb
{
Eina_Bool from_client : 1;
unsigned int id, hdl;
unsigned int stride, size;
int fd;
void *mmap;
#ifdef HAVE_GBM
struct gbm_bo *bo;
#endif
} Ecore_Drm_Fb;
/* opaque structure to represent a drm device */
typedef struct _Ecore_Drm_Device Ecore_Drm_Device;
/* opaque structure to represent a drm output mode */
typedef struct _Ecore_Drm_Output_Mode Ecore_Drm_Output_Mode;
/* opaque structure to represent a drm output */
typedef struct _Ecore_Drm_Output Ecore_Drm_Output;
/* opaque structure to represent a drm udev input */
typedef struct _Ecore_Drm_Input Ecore_Drm_Input;
/* opaque structure to represent a drm evdev input */
typedef struct _Ecore_Drm_Evdev Ecore_Drm_Evdev;
/* opaque structure to represent a drm seat */
typedef struct _Ecore_Drm_Seat Ecore_Drm_Seat;
/* opaque structure to represent a drm sprite */
typedef struct _Ecore_Drm_Sprite Ecore_Drm_Sprite;
/**
* @file
* @brief Ecore functions for dealing with drm, virtual terminals
*
* @defgroup Ecore_Drm_Group Ecore_Drm - Drm Integration
* @ingroup Ecore
*
* Ecore_Drm provides a wrapper and functions for using libdrm
*
* @li @ref Ecore_Drm_Init_Group
* @li @ref Ecore_Drm_Device_Group
* @li @ref Ecore_Drm_Tty_Group
* @li @ref Ecore_Drm_Output_Group
* @li @ref Ecore_Drm_Input_Group
* @li @ref Ecore_Drm_Sprite_Group
*
*/
EAPI int ecore_drm_init(void);
EAPI int ecore_drm_shutdown(void);
EAPI void *ecore_drm_gbm_get(Ecore_Drm_Device *dev);
EAPI unsigned int ecore_drm_gbm_format_get(Ecore_Drm_Device *dev);
EAPI Ecore_Drm_Device *ecore_drm_device_find(const char *name, const char *seat);
EAPI void ecore_drm_device_free(Ecore_Drm_Device *dev);
EAPI Eina_Bool ecore_drm_device_open(Ecore_Drm_Device *dev);
EAPI Eina_Bool ecore_drm_device_close(Ecore_Drm_Device *dev);
EAPI Eina_Bool ecore_drm_device_master_get(Ecore_Drm_Device *dev);
EAPI Eina_Bool ecore_drm_device_master_set(Ecore_Drm_Device *dev);
EAPI Eina_Bool ecore_drm_device_master_drop(Ecore_Drm_Device *dev);
EAPI int ecore_drm_device_fd_get(Ecore_Drm_Device *dev);
EAPI Eina_Bool ecore_drm_tty_open(Ecore_Drm_Device *dev, const char *name);
EAPI Eina_Bool ecore_drm_tty_close(Ecore_Drm_Device *dev);
EAPI Eina_Bool ecore_drm_tty_release(Ecore_Drm_Device *dev);
EAPI Eina_Bool ecore_drm_tty_acquire(Ecore_Drm_Device *dev);
EAPI Eina_Bool ecore_drm_outputs_create(Ecore_Drm_Device *dev);
EAPI void ecore_drm_output_free(Ecore_Drm_Output *output);
EAPI void ecore_drm_output_cursor_size_set(Ecore_Drm_Output *output, int handle, int w, int h);
EAPI Eina_Bool ecore_drm_output_enable(Ecore_Drm_Output *output);
EAPI void ecore_drm_output_fb_release(Ecore_Drm_Output *output, Ecore_Drm_Fb *fb);
EAPI void ecore_drm_output_repaint(Ecore_Drm_Output *output);
EAPI Eina_Bool ecore_drm_inputs_create(Ecore_Drm_Device *dev);
EAPI void ecore_drm_inputs_destroy(Ecore_Drm_Device *dev);
EAPI Eina_Bool ecore_drm_inputs_enable(Ecore_Drm_Input *input);
EAPI void ecore_drm_inputs_disable(Ecore_Drm_Input *input);
EAPI Eina_Bool ecore_drm_sprites_create(Ecore_Drm_Device *dev);
EAPI void ecore_drm_sprites_destroy(Ecore_Drm_Device *dev);
EAPI void ecore_drm_sprites_fb_set(Ecore_Drm_Sprite *sprite, int fb_id, int flags);
EAPI Eina_Bool ecore_drm_sprites_crtc_supported(Ecore_Drm_Output *output, unsigned int supported);
EAPI Ecore_Drm_Fb *ecore_drm_fb_create(Ecore_Drm_Device *dev, int width, int height);
EAPI void ecore_drm_fb_destroy(Ecore_Drm_Fb *fb);
#endif

View File

@ -0,0 +1,414 @@
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "ecore_drm_private.h"
#include <sys/wait.h>
#include <sys/socket.h>
#include <signal.h>
#define RIGHTS_LEN CMSG_LEN(sizeof(int))
#define IOVSET(_iov, _addr, _len) \
(_iov)->iov_base = (void *)(_addr); \
(_iov)->iov_len = (_len);
/* local variables */
static int _ecore_drm_init_count = 0;
static int _ecore_drm_sockets[2] = { -1, -1 };
static struct cmsghdr *cmsgptr = NULL;
/* external variables */
struct udev *udev;
int _ecore_drm_log_dom = -1;
#ifdef LOG_TO_FILE
FILE *lg;
#endif
static Eina_Bool
_ecore_drm_sockets_create(void)
{
if (_ecore_drm_sockets[0] > -1) return EINA_TRUE;
/* create a pair of sequenced sockets (fixed-length)
* NB: when reading from one of these, it is required that we read
* an entire packet with each read() call */
if (socketpair(AF_LOCAL, SOCK_SEQPACKET | SOCK_NONBLOCK,
0, _ecore_drm_sockets) < 0)
{
ERR("Socketpair Failed: %m");
return EINA_FALSE;
}
/* NB: We don't want cloexec for the sockets. That would cause them to be
* closed when we exec the child process but we need them open so that
* we can pass messages */
/* if (fcntl(_ecore_drm_sockets[0], F_SETFD, FD_CLOEXEC) < 0) */
/* { */
/* ERR("Failed to set CLOEXEC: %m"); */
/* return EINA_FALSE; */
/* } */
/* DBG("Parent Socket: %d", _ecore_drm_sockets[0]); */
/* DBG("Child Socket: %d", _ecore_drm_sockets[1]); */
return EINA_TRUE;
}
static Eina_Bool
_ecore_drm_launcher_spawn(void)
{
pid_t pid;
if ((pid = fork()) < 0) return EINA_FALSE;
if (pid == 0)
{
char renv[64], wenv[64], buff[PATH_MAX];
char *args[1] = { NULL };
sigset_t mask;
/* read socket for slave is 1 */
snprintf(renv, sizeof(renv), "ECORE_DRM_LAUNCHER_SOCKET_READ=%d",
_ecore_drm_sockets[1]);
/* write socket for slave is 0 */
snprintf(wenv, sizeof(wenv), "ECORE_DRM_LAUNCHER_SOCKET_WRITE=%d",
_ecore_drm_sockets[0]);
/* assemble exec path */
snprintf(buff, sizeof(buff),
"%s/ecore_drm/bin/%s/ecore_drm_launch",
PACKAGE_LIB_DIR, MODULE_ARCH);
/* don't give our signal mask to the child */
sigemptyset(&mask);
sigaddset(&mask, SIGTERM);
sigaddset(&mask, SIGCHLD);
sigaddset(&mask, SIGINT);
sigaddset(&mask, SIGTTIN);
sigaddset(&mask, SIGTTOU);
sigprocmask(SIG_UNBLOCK, &mask, NULL);
/* NB: We need to use execve here so that capabilities are inherited.
* Also, this should set Our (ecore_drm) effective uid to be the
* owner of the launched process (setuid in this case) */
char *ev[3] = { strdup(renv), strdup(wenv), NULL };
execve(buff, args, ev);
}
else
{
int status;
while (waitpid(pid, &status, WNOHANG) < 0)
if (errno != EINTR) break;
return EINA_TRUE;
}
return EINA_FALSE;
}
static ssize_t
_ecore_drm_socket_send(int opcode, int fd, void *data, size_t bytes)
{
Ecore_Drm_Message dmsg;
struct iovec iov[2];
struct msghdr msg;
ssize_t size;
/* Simplified version of sending messages. We don't need to send any
* 'credentials' with this as it is just basically an IPC to send over
* our request to the slave process */
/* NB: Hmm, don't think we need to set any socket options here */
memset(&dmsg, 0, sizeof(dmsg));
IOVSET(iov + 0, &dmsg, sizeof(dmsg));
IOVSET(iov + 1, &data, bytes);
dmsg.opcode = opcode;
dmsg.size = bytes;
msg.msg_name = NULL;
msg.msg_namelen = 0;
msg.msg_iov = iov;
msg.msg_iovlen = 2;
msg.msg_flags = 0;
if ((!cmsgptr) && (!(cmsgptr = malloc(RIGHTS_LEN))))
return -1;
cmsgptr->cmsg_level = SOL_SOCKET;
cmsgptr->cmsg_type = SCM_RIGHTS;
cmsgptr->cmsg_len = RIGHTS_LEN;
msg.msg_control = cmsgptr;
msg.msg_controllen = RIGHTS_LEN;
if (fd > -1)
*((int *)CMSG_DATA(cmsgptr)) = fd;
else
*((int *)CMSG_DATA(cmsgptr)) = _ecore_drm_sockets[1];
errno = 0;
size = sendmsg(_ecore_drm_sockets[1], &msg, MSG_EOR);
if (errno != 0)
{
DBG("Error Sending Message: %m");
}
/* DBG("Sent %li bytes to Socket %d", size, _ecore_drm_sockets[1]); */
return size;
}
static int
_ecore_drm_socket_receive(int opcode EINA_UNUSED, int *fd, void **data, size_t bytes)
{
int ret = ECORE_DRM_OP_FAILURE;
Ecore_Drm_Message dmsg;
struct cmsghdr *cmsg;
struct iovec iov[2];
struct msghdr msg;
char buff[CMSG_SPACE(sizeof(fd))];
/* ssize_t size; */
memset(&dmsg, 0, sizeof(dmsg));
memset(&buff, 0, sizeof(buff));
IOVSET(iov + 0, &dmsg, sizeof(dmsg));
IOVSET(iov + 1, &buff, sizeof(buff));
msg.msg_name = NULL;
msg.msg_namelen = 0;
msg.msg_iov = iov;
msg.msg_iovlen = 2;
msg.msg_flags = 0;
if ((!cmsgptr) && (!(cmsgptr = malloc(RIGHTS_LEN))))
return -1;
msg.msg_control = cmsgptr;
msg.msg_controllen = RIGHTS_LEN;
errno = 0;
recvmsg(_ecore_drm_sockets[0], &msg, 0);
if (errno != 0)
{
ERR("Failed to receive message: %m");
return -1;
}
/* DBG("Received %li bytes from %d", size, _ecore_drm_sockets[0]); */
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
cmsg = CMSG_NXTHDR(&msg, cmsg))
{
if (cmsg->cmsg_level != SOL_SOCKET)
continue;
switch (cmsg->cmsg_type)
{
case SCM_RIGHTS:
if (fd) *fd = *((int *)CMSG_DATA(cmsg));
switch (dmsg.opcode)
{
case ECORE_DRM_OP_DEVICE_OPEN:
case ECORE_DRM_OP_DEVICE_CLOSE:
case ECORE_DRM_OP_TTY_OPEN:
case ECORE_DRM_OP_DEVICE_MASTER_DROP:
case ECORE_DRM_OP_DEVICE_MASTER_SET:
if ((fd) && (*fd >= 0)) ret = ECORE_DRM_OP_SUCCESS;
if (data) memcpy(*data, buff, bytes);
break;
default:
break;
}
break;
default:
break;
}
}
return ret;
}
void
_ecore_drm_message_send(int opcode, int fd, void *data, size_t bytes)
{
_ecore_drm_socket_send(opcode, fd, data, bytes);
}
Eina_Bool
_ecore_drm_message_receive(int opcode, int *fd, void **data, size_t bytes)
{
int ret = ECORE_DRM_OP_FAILURE;
ret = _ecore_drm_socket_receive(opcode, fd, data, bytes);
if (ret != ECORE_DRM_OP_SUCCESS) return EINA_FALSE;
return EINA_TRUE;
}
/**
* @defgroup Ecore_Drm_Init_Group Drm Library Init and Shutdown Functions
*
* Functions that start and shutdown the Ecore_Drm Library.
*/
/**
* Initialize the Ecore_Drm library
*
* @return The number of times the library has been initialized without
* being shut down. 0 is returned if an error occurs.
*
* @ingroup Ecore_Drm_Init_Group
*/
EAPI int
ecore_drm_init(void)
{
/* if we have already initialized, return the count */
if (++_ecore_drm_init_count != 1) return _ecore_drm_init_count;
/* try to init eina */
if (!eina_init()) return --_ecore_drm_init_count;
if (!ecore_init())
{
eina_shutdown();
return --_ecore_drm_init_count;
}
/* set logging level */
eina_log_level_set(EINA_LOG_LEVEL_DBG);
setvbuf(stdout, NULL, _IONBF, 0);
setvbuf(stderr, NULL, _IONBF, 0);
/* optionally log output to a file */
#ifdef LOG_TO_FILE
int log_fd;
char log_path[PATH_MAX];
mode_t um;
/* assemble logging file path */
strcpy(log_path, "/tmp/ecore_drm_XXXXXX");
/* create temporary logging file */
um = umask(S_IRWXG | S_IRWXO);
log_fd = mkstemp(log_path);
umask(um);
/* try to open logging file */
if (!(lg = fdopen(log_fd, "w")))
goto log_err;
eina_log_print_cb_set(eina_log_print_cb_file, lg);
#endif
/* try to create logging domain */
_ecore_drm_log_dom =
eina_log_domain_register("ecore_drm", ECORE_DRM_DEFAULT_LOG_COLOR);
if (!_ecore_drm_log_dom)
{
EINA_LOG_ERR("Could not create log domain for Ecore_Drm");
goto log_err;
}
/* set default logging level for this domain */
if (!eina_log_domain_level_check(_ecore_drm_log_dom, EINA_LOG_LEVEL_DBG))
eina_log_domain_level_set("ecore_drm", EINA_LOG_LEVEL_DBG);
/* try to init udev */
if (!(udev = udev_new()))
goto udev_err;
/* try to create the socketpair */
if (!_ecore_drm_sockets_create())
goto sock_err;
/* try to run Spartacus */
if (!_ecore_drm_launcher_spawn())
goto spawn_err;
/* return init count */
return _ecore_drm_init_count;
spawn_err:
close(_ecore_drm_sockets[0]);
close(_ecore_drm_sockets[1]);
sock_err:
if (udev) udev_unref(udev);
udev_err:
ecore_shutdown();
eina_log_domain_unregister(_ecore_drm_log_dom);
_ecore_drm_log_dom = -1;
log_err:
#ifdef LOG_TO_FILE
if (lg) fclose(lg);
#endif
/* shutdown eina */
eina_shutdown();
return --_ecore_drm_init_count;
}
/**
* Shutdown the Ecore_Drm library.
*
* @return The number of times the library has been initialized without
* being shutdown. 0 is returned if an error occurs.
*
* @ingroup Ecore_Drm_Init_Group
*/
EAPI int
ecore_drm_shutdown(void)
{
/* if we are still in use, decrement init count and get out */
if (--_ecore_drm_init_count != 0) return _ecore_drm_init_count;
/* close udev handle */
if (udev) udev_unref(udev);
/* close sockets */
close(_ecore_drm_sockets[0]);
close(_ecore_drm_sockets[1]);
ecore_shutdown();
/* unregsiter log domain */
eina_log_domain_unregister(_ecore_drm_log_dom);
_ecore_drm_log_dom = -1;
#ifdef LOG_TO_FILE
if (lg) fclose(lg);
#endif
/* shutdown eina */
eina_shutdown();
/* return init count */
return _ecore_drm_init_count;
}
EAPI void *
ecore_drm_gbm_get(Ecore_Drm_Device *dev)
{
if (!dev) return NULL;
#ifdef HAVE_GBM
return dev->gbm;
#endif
return NULL;
}
EAPI unsigned int
ecore_drm_gbm_format_get(Ecore_Drm_Device *dev)
{
if (!dev) return 0;
return dev->format;
}

View File

@ -0,0 +1,585 @@
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "ecore_drm_private.h"
#include <dlfcn.h>
#ifdef HAVE_GBM
static Eina_Bool
_ecore_drm_device_egl_config_get(Ecore_Drm_Device *dev, const EGLint *attribs, const EGLint *visual)
{
EGLint c = 0, m = 0;
EGLConfig *cfgs;
int i = 0;
if (!eglGetConfigs(dev->egl.disp, NULL, 0, &c) || (c < 1))
return EINA_FALSE;
if (!(cfgs = calloc(c, sizeof(*cfgs)))) return EINA_FALSE;
if (!eglChooseConfig(dev->egl.disp, attribs, cfgs, c, &m))
{
free(cfgs);
return EINA_FALSE;
}
for (i = 0; i < m; i++)
{
EGLint id;
if (visual)
{
if (!eglGetConfigAttrib(dev->egl.disp, cfgs[i],
EGL_NATIVE_VISUAL_ID, &id))
continue;
if ((id != 0) && (id != *visual))
continue;
}
dev->egl.cfg = cfgs[i];
free(cfgs);
return EINA_TRUE;
}
free(cfgs);
return EINA_FALSE;
}
#endif
static void
_ecore_drm_device_cb_page_flip(int fd EINA_UNUSED, unsigned int frame EINA_UNUSED, unsigned int sec EINA_UNUSED, unsigned int usec EINA_UNUSED, void *data)
{
Ecore_Drm_Output *output;
DBG("Drm Page Flip Event");
if (!(output = data)) return;
if (output->pending_flip)
{
ecore_drm_output_fb_release(output, output->current);
output->current = output->next;
output->next = NULL;
}
output->pending_flip = EINA_FALSE;
if (!output->pending_vblank) ecore_drm_output_repaint(output);
}
static void
_ecore_drm_device_cb_vblank(int fd EINA_UNUSED, unsigned int frame EINA_UNUSED, unsigned int sec EINA_UNUSED, unsigned int usec EINA_UNUSED, void *data)
{
Ecore_Drm_Sprite *sprite;
Ecore_Drm_Output *output;
DBG("Drm VBlank Event");
if (!(sprite = data)) return;
output = sprite->output;
output->pending_vblank = EINA_FALSE;
ecore_drm_output_fb_release(output, sprite->current_fb);
sprite->current_fb = sprite->next_fb;
sprite->next_fb = NULL;
if (!output->pending_flip) _ecore_drm_output_frame_finish(output);
}
static Eina_Bool
_ecore_drm_device_cb_event(void *data, Ecore_Fd_Handler *hdlr EINA_UNUSED)
{
Ecore_Drm_Device *dev;
drmEventContext ctx;
if (!(dev = data)) return ECORE_CALLBACK_RENEW;
DBG("Drm Device Event");
memset(&ctx, 0, sizeof(ctx));
ctx.version = DRM_EVENT_CONTEXT_VERSION;
ctx.page_flip_handler = _ecore_drm_device_cb_page_flip;
ctx.vblank_handler = _ecore_drm_device_cb_vblank;
drmHandleEvent(dev->drm.fd, &ctx);
return ECORE_CALLBACK_RENEW;
}
static Eina_Bool
_ecore_drm_device_cb_idle(void *data)
{
Ecore_Drm_Device *dev;
Ecore_Drm_Output *output;
Eina_List *l;
if (!(dev = data)) return ECORE_CALLBACK_CANCEL;
EINA_LIST_FOREACH(dev->outputs, l, output)
{
output->need_repaint = EINA_TRUE;
if (output->repaint_scheduled) continue;
_ecore_drm_output_repaint_start(output);
}
return ECORE_CALLBACK_RENEW;
}
/**
* @defgroup Ecore_Drm_Device_Group Device manipulation functions
*
* Functions that deal with finding, opening, closing, and otherwise using
* the DRM device itself.
*/
/**
* Find a drm device in the system.
*
* @param name The name of the device to find. If NULL, this function will
* search for the default drm device.
* @param seat The name of the seat where this device may be found. If NULL,
* this function will use a default seat name 'seat0'.
*
* @return An opaque Ecore_Drm_Device structure representing the card.
*
* @ingroup Ecore_Drm_Device_Group
*/
EAPI Ecore_Drm_Device *
ecore_drm_device_find(const char *name, const char *seat)
{
Ecore_Drm_Device *dev = NULL;
struct udev_enumerate *uenum;
struct udev_list_entry *uentry;
struct udev_device *udevice, *tmpdevice = NULL, *pcidevice;
const char *path = NULL, *devseat = NULL;
/* check for existing udev reference */
if (!udev) return NULL;
/* setup udev enumerator */
uenum = udev_enumerate_new(udev);
udev_enumerate_add_match_subsystem(uenum, "drm");
udev_enumerate_add_match_subsystem(uenum, "card[0-9]*");
/* ask udev for list of drm devices */
udev_enumerate_scan_devices(uenum);
/* loop list of returned devices */
udev_list_entry_foreach(uentry, udev_enumerate_get_list_entry(uenum))
{
/* get device path */
path = udev_list_entry_get_name(uentry);
/* get udev device */
if (!(udevice = udev_device_new_from_syspath(udev, path)))
continue;
/* if we are looking for a certain device, then compare names */
if (name)
{
if (strcmp(name, udev_device_get_devnode(udevice)))
{
udev_device_unref(udevice);
continue;
}
}
/* get this devices' seat */
devseat = udev_device_get_property_value(udevice, "ID_SEAT");
if (!devseat) devseat = "seat0";
/* if we are looking for a device on a certain seat, compare it */
if (seat)
{
if (strcmp(seat, devseat))
{
udev_device_unref(udevice);
continue;
}
}
else
{
/* no seat name passed to use. check default */
if (strcmp(devseat, "seat0"))
{
udev_device_unref(udevice);
continue;
}
}
/* try to find the boot_vga attribute */
if ((pcidevice =
udev_device_get_parent_with_subsystem_devtype(udevice, "pci", NULL)))
{
const char *id;
if ((id = udev_device_get_sysattr_value(pcidevice, "boot_vga")))
{
if (!strcmp(id, "1"))
{
if (tmpdevice) udev_device_unref(tmpdevice);
tmpdevice = udevice;
break;
}
}
}
if (!tmpdevice)
tmpdevice = udevice;
else
udev_device_unref(udevice);
}
/* destroy the enumerator */
udev_enumerate_unref(uenum);
if (tmpdevice)
{
DBG("Found Drm Device");
DBG("\tFilename: %s", udev_device_get_devnode(tmpdevice));
DBG("\tDriver: %s", udev_device_get_driver(tmpdevice));
DBG("\tDevpath: %s", udev_device_get_devpath(tmpdevice));
DBG("\tSyspath: %s", udev_device_get_syspath(tmpdevice));
DBG("\tSysname: %s", udev_device_get_sysname(tmpdevice));
/* try to allocate space for return device structure */
if ((dev = calloc(1, sizeof(Ecore_Drm_Device))))
{
const char *id, *seat;
/* set device name */
dev->drm.name =
eina_stringshare_add(udev_device_get_devnode(tmpdevice));
/* set device path */
dev->drm.path =
eina_stringshare_add(udev_device_get_syspath(tmpdevice));
/* store id for this device */
if ((id = udev_device_get_sysnum(tmpdevice)))
dev->id = atoi(id);
/* set dev seat */
seat = udev_device_get_property_value(tmpdevice, "ID_SEAT");
if (!seat) seat = "seat0";
dev->seat = eina_stringshare_add(seat);
dev->format = GBM_FORMAT_XRGB8888;
dev->use_hw_accel = EINA_FALSE;
}
}
/* release device reference */
udev_device_unref(tmpdevice);
return dev;
}
/**
* Free an Ecore_Drm_Device
*
* This function will cleanup and free any previously allocated Ecore_Drm_Device.
*
* @param dev The Ecore_Drm_Device to free
*
* @ingroup Ecore_Drm_Device_Group
*/
EAPI void
ecore_drm_device_free(Ecore_Drm_Device *dev)
{
Ecore_Drm_Output *output;
/* check for valid device */
if (!dev) return;
/* free outputs */
EINA_LIST_FREE(dev->outputs, output)
ecore_drm_output_free(output);
/* free crtcs */
if (dev->crtcs) free(dev->crtcs);
/* free device name */
if (dev->drm.name) eina_stringshare_del(dev->drm.name);
/* free device path */
if (dev->drm.path) eina_stringshare_del(dev->drm.path);
/* free device seat */
if (dev->seat) eina_stringshare_del(dev->seat);
/* free structure */
free(dev);
}
/**
* Open an Ecore_Drm_Device
*
* This function will open an existing Ecore_Drm_Device for use.
*
* @param dev The Ecore_Drm_Device to try and open
*
* @return EINA_TRUE on success, EINA_FALSE on failure
*
* @ingroup Ecore_Drm_Device_Group
*/
EAPI Eina_Bool
ecore_drm_device_open(Ecore_Drm_Device *dev)
{
uint64_t caps;
/* check for valid device */
if ((!dev) || (!dev->drm.name)) return EINA_FALSE;
dev->drm.fd = open(dev->drm.name, O_RDWR);
if (dev->drm.fd < 0) return EINA_FALSE;
DBG("Opened Device %s : %d", dev->drm.name, dev->drm.fd);
if (!drmGetCap(dev->drm.fd, DRM_CAP_TIMESTAMP_MONOTONIC, &caps))
{
if (caps == 1)
dev->drm.clock = CLOCK_MONOTONIC;
else
dev->drm.clock = CLOCK_REALTIME;
}
else
{
ERR("Could not get device capabilities: %m");
}
#ifdef HAVE_GBM
if (getenv("ECORE_DRM_HW_ACCEL"))
{
/* Typically, gbm loads the dri driver However some versions of Mesa
* do not have libglapi symbols linked in the driver. Because of this,
* using hardware accel for our drm code Could fail with a
* message that the driver could not load. Let's be proactive and
* work around this for the user by preloading the glapi library */
dlopen("libglapi.so.0", (RTLD_LAZY | RTLD_GLOBAL));
if ((dev->gbm = gbm_create_device(dev->drm.fd)))
{
EGLint major, minor, visual;
const EGLint attribs[] =
{
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
EGL_RED_SIZE, 1, EGL_GREEN_SIZE, 1,
EGL_BLUE_SIZE, 1, EGL_ALPHA_SIZE, 0,
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, EGL_NONE
};
dev->use_hw_accel = EINA_TRUE;
/* TODO: gbm_device_is_format_supported to check that
* argb can be used for scanout | rendering */
dev->format = GBM_FORMAT_XRGB8888;
dev->egl.disp = eglGetDisplay(dev->gbm);
if (dev->egl.disp == EGL_NO_DISPLAY)
{
ERR("Could not get egl display");
goto init_software;
}
if (!eglInitialize(dev->egl.disp, &major, &minor))
{
ERR("Could not initialize egl");
goto init_software;
}
visual = dev->format;
if (!_ecore_drm_device_egl_config_get(dev, attribs, &visual))
{
ERR("Could not get egl config");
goto init_software;
}
}
else
{
WRN("Failed to create gbm device");
goto init_software;
}
}
else
#endif
{
/* TODO: init software */
init_software:
DBG("Init Software Engine");
#ifdef HAVE_GBM
if (dev->egl.disp)
{
eglMakeCurrent(dev->egl.disp, EGL_NO_SURFACE, EGL_NO_SURFACE,
EGL_NO_CONTEXT);
eglTerminate(dev->egl.disp);
eglReleaseThread();
}
if (dev->gbm) gbm_device_destroy(dev->gbm);
dev->gbm = NULL;
#endif
}
dev->drm.hdlr =
ecore_main_fd_handler_add(dev->drm.fd, ECORE_FD_READ,
_ecore_drm_device_cb_event, dev, NULL, NULL);
dev->drm.idler =
ecore_idle_enterer_add(_ecore_drm_device_cb_idle, dev);
return EINA_TRUE;
}
/**
* Close an Ecore_Drm_Device
*
* This function will close a previously opened Ecore_Drm_Device
*
* @param dev The Ecore_Drm_Device to free
*
* @return EINA_TRUE on success, EINA_FALSE on failure
*
* @ingroup Ecore_Drm_Device_Group
*/
EAPI Eina_Bool
ecore_drm_device_close(Ecore_Drm_Device *dev)
{
/* check for valid device */
if (!dev) return EINA_FALSE;
#ifdef HAVE_GBM
if (dev->use_hw_accel)
{
if (dev->egl.disp)
{
eglMakeCurrent(dev->egl.disp, EGL_NO_SURFACE, EGL_NO_SURFACE,
EGL_NO_CONTEXT);
eglTerminate(dev->egl.disp);
eglReleaseThread();
}
if (dev->gbm) gbm_device_destroy(dev->gbm);
dev->gbm = NULL;
}
#endif
if (dev->drm.hdlr) ecore_main_fd_handler_del(dev->drm.hdlr);
dev->drm.hdlr = NULL;
close(dev->drm.fd);
/* reset device fd */
dev->drm.fd = -1;
/* free(data); */
return EINA_TRUE;
}
/**
* Get if a given Ecore_Drm_Device is master
*
* This function will check if the given drm device is set to master
*
* @param dev The Ecore_Drm_Device to check
*
* @return EINA_TRUE if device is master, EINA_FALSE otherwise
*
* @ingroup Ecore_Drm_Device_Group
*/
EAPI Eina_Bool
ecore_drm_device_master_get(Ecore_Drm_Device *dev)
{
/* drm_magic_t mag; */
/* check for valid device */
if ((!dev) || (dev->drm.fd < 0)) return EINA_FALSE;
/* FIXME: Remote this to the slave process !! */
/* get if we are master or not */
/* if ((drmGetMagic(dev->drm.fd, &mag) == 0) && */
/* (drmAuthMagic(dev->drm.fd, mag) == 0)) */
/* return EINA_TRUE; */
return EINA_FALSE;
}
/**
* Set a given Ecore_Drm_Device to master
*
* This function will attempt to set a given drm device to be master
*
* @param dev The Ecore_Drm_Device to set
*
* @return EINA_TRUE on success, EINA_FALSE on failure
*
* @ingroup Ecore_Drm_Device_Group
*/
EAPI Eina_Bool
ecore_drm_device_master_set(Ecore_Drm_Device *dev)
{
Eina_Bool ret = EINA_FALSE;
int dfd;
/* check for valid device */
if ((!dev) || (dev->drm.fd < 0)) return EINA_FALSE;
DBG("Set Master On Fd: %d", dev->drm.fd);
/* try to close the device */
_ecore_drm_message_send(ECORE_DRM_OP_DEVICE_MASTER_SET, dev->drm.fd,
NULL, 0);
/* get the result of the close operation */
ret = _ecore_drm_message_receive(ECORE_DRM_OP_DEVICE_MASTER_SET, &dfd,
NULL, 0);
if (!ret) return EINA_FALSE;
return EINA_TRUE;
}
/**
* Tell a given Ecore_Drm_Device to stop being master
*
* This function will attempt to ask a drm device to stop being master
*
* @param dev The Ecore_Drm_Device to set
*
* @return EINA_TRUE on success, EINA_FALSE on failure
*
* @ingroup Ecore_Drm_Device_Group
*/
EAPI Eina_Bool
ecore_drm_device_master_drop(Ecore_Drm_Device *dev)
{
Eina_Bool ret = EINA_FALSE;
int dfd;
/* check for valid device */
if ((!dev) || (dev->drm.fd < 0)) return EINA_FALSE;
DBG("Drop Master On Fd: %d", dev->drm.fd);
/* try to close the device */
_ecore_drm_message_send(ECORE_DRM_OP_DEVICE_MASTER_DROP, dev->drm.fd,
NULL, 0);
/* get the result of the close operation */
ret = _ecore_drm_message_receive(ECORE_DRM_OP_DEVICE_MASTER_DROP, &dfd,
NULL, 0);
if (!ret) return EINA_FALSE;
return EINA_TRUE;
}
EAPI int
ecore_drm_device_fd_get(Ecore_Drm_Device *dev)
{
if (!dev) return -1;
return dev->drm.fd;
}

View File

@ -0,0 +1,360 @@
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
/* copied from udev/extras/input_id/input_id.c */
/* we must use this kernel-compatible implementation */
#define BITS_PER_LONG (sizeof(unsigned long) * 8)
#define NBITS(x) ((((x)-1)/BITS_PER_LONG)+1)
#define OFF(x) ((x)%BITS_PER_LONG)
#define BIT(x) (1UL<<OFF(x))
#define LONG(x) ((x)/BITS_PER_LONG)
#define TEST_BIT(array, bit) ((array[LONG(bit)] >> OFF(bit)) & 1)
/* end copied */
#include "ecore_drm_private.h"
#include <sys/ioctl.h>
#include <linux/input.h>
/* local functions */
static Eina_Bool
_device_configure(Ecore_Drm_Evdev *edev)
{
Eina_Bool ret = EINA_FALSE;
if (!edev) return EINA_FALSE;
if ((edev->caps & (EVDEV_MOTION_ABS | EVDEV_MOTION_REL)) &&
(edev->caps & EVDEV_BUTTON))
{
DBG("Input device %s is a pointer", edev->name);
edev->seat_caps |= EVDEV_SEAT_POINTER;
ret = EINA_TRUE;
}
if (edev->caps & EVDEV_KEYBOARD)
{
DBG("Input device %s is a keyboard", edev->name);
edev->seat_caps |= EVDEV_SEAT_KEYBOARD;
ret = EINA_TRUE;
}
if (edev->caps & EVDEV_TOUCH)
{
DBG("Input device %s is a touchpad", edev->name);
edev->seat_caps |= EVDEV_SEAT_TOUCH;
ret = EINA_TRUE;
}
return ret;
}
static Eina_Bool
_device_handle(Ecore_Drm_Evdev *edev)
{
struct input_absinfo absinfo;
unsigned long dev_bits[NBITS(EV_MAX)];
unsigned long abs_bits[NBITS(ABS_MAX)];
unsigned long rel_bits[NBITS(REL_MAX)];
unsigned long key_bits[NBITS(KEY_MAX)];
/* Eina_Bool have_key = EINA_FALSE; */
Eina_Bool have_abs = EINA_FALSE;
if (!edev) return EINA_FALSE;
ioctl(edev->fd, EVIOCGBIT(0, sizeof(dev_bits)), dev_bits);
if (TEST_BIT(dev_bits, EV_ABS))
{
have_abs = EINA_TRUE;
ioctl(edev->fd, EVIOCGBIT(EV_ABS, sizeof(abs_bits)), abs_bits);
if ((TEST_BIT(abs_bits, ABS_WHEEL)) ||
(TEST_BIT(abs_bits, ABS_GAS)) ||
(TEST_BIT(abs_bits, ABS_BRAKE)) ||
(TEST_BIT(abs_bits, ABS_HAT0X)))
{
/* ignore joystick */
return EINA_FALSE;
}
if (TEST_BIT(abs_bits, ABS_X))
{
ioctl(edev->fd, EVIOCGABS(ABS_X), &absinfo);
edev->abs.min_x = absinfo.minimum;
edev->abs.max_x = absinfo.maximum;
edev->caps |= EVDEV_MOTION_ABS;
}
if (TEST_BIT(abs_bits, ABS_Y))
{
ioctl(edev->fd, EVIOCGABS(ABS_Y), &absinfo);
edev->abs.min_y = absinfo.minimum;
edev->abs.max_y = absinfo.maximum;
edev->caps |= EVDEV_MOTION_ABS;
}
if ((TEST_BIT(abs_bits, ABS_MT_POSITION_X)) &&
(TEST_BIT(abs_bits, ABS_MT_POSITION_Y)))
{
DBG("Handle MultiTouch Device: %s", edev->path);
}
}
if (TEST_BIT(dev_bits, EV_REL))
{
ioctl(edev->fd, EVIOCGBIT(EV_REL, sizeof(rel_bits)), rel_bits);
if ((TEST_BIT(rel_bits, REL_X)) || (TEST_BIT(rel_bits, REL_Y)))
edev->caps |= EVDEV_MOTION_REL;
}
if (TEST_BIT(dev_bits, EV_KEY))
{
unsigned int i = 0;
/* have_key = EINA_TRUE; */
ioctl(edev->fd, EVIOCGBIT(EV_KEY, sizeof(key_bits)), key_bits);
if (have_abs)
{
if ((TEST_BIT(key_bits, BTN_TOOL_FINGER)) &&
(!TEST_BIT(key_bits, BTN_TOOL_PEN)))
{
DBG("Device Is Touchpad: %s", edev->path);
}
}
for (i = KEY_ESC; i < KEY_MAX; i++)
{
if ((i >= BTN_MISC) && (i < KEY_OK)) continue;
if (TEST_BIT(key_bits, i))
{
edev->caps |= EVDEV_KEYBOARD;
break;
}
}
if (TEST_BIT(key_bits, BTN_TOUCH))
edev->caps |= EVDEV_TOUCH;
for (i = BTN_MISC; i < BTN_JOYSTICK; i++)
{
if (TEST_BIT(key_bits, i))
{
edev->caps |= EVDEV_BUTTON;
edev->caps &= ~EVDEV_TOUCH;
break;
}
}
}
if (TEST_BIT(dev_bits, EV_LED)) edev->caps |= EVDEV_KEYBOARD;
return EINA_TRUE;
}
static void
_device_notify_key(Ecore_Drm_Evdev *dev, struct input_event *event, unsigned int timestamp)
{
DBG("Key Event");
DBG("\tCode: %d", event->code);
DBG("\tValue: %d", event->value);
if ((event->code >= KEY_ESC) && (event->code <= KEY_COMPOSE))
{
/* ignore key repeat */
if (event->value == 2)
{
DBG("\tKey Repeat");
}
}
}
static void
_device_process_flush(Ecore_Drm_Evdev *dev, unsigned int timestamp)
{
switch (dev->pending_event)
{
case EVDEV_NONE:
return;
case EVDEV_RELATIVE_MOTION:
goto out;
break;
case EVDEV_ABSOLUTE_MT_DOWN:
goto out;
break;
case EVDEV_ABSOLUTE_MT_MOTION:
goto out;
break;
case EVDEV_ABSOLUTE_MT_UP:
goto out;
break;
case EVDEV_ABSOLUTE_TOUCH_DOWN:
goto out;
break;
case EVDEV_ABSOLUTE_MOTION:
goto out;
break;
case EVDEV_ABSOLUTE_TOUCH_UP:
goto out;
break;
}
out:
dev->pending_event = EVDEV_NONE;
}
static void
_device_process_key(Ecore_Drm_Evdev *dev, struct input_event *event, unsigned int timestamp)
{
if (event->code == BTN_TOUCH)
{
/* TODO: check for mt device */
return;
}
_device_process_flush(dev, timestamp);
switch (event->code)
{
case BTN_LEFT:
case BTN_RIGHT:
case BTN_MIDDLE:
case BTN_SIDE:
case BTN_EXTRA:
case BTN_FORWARD:
case BTN_BACK:
case BTN_TASK:
/* TODO: notify button */
break;
default:
_device_notify_key(dev, event, timestamp);
break;
}
}
static void
_device_process(Ecore_Drm_Evdev *dev, struct input_event *event, int count)
{
struct input_event *ev, *end;
unsigned int timestamp = 0;
DBG("Evdev Device Process");
ev = event;
end = ev + count;
for (ev = event; ev < end; ev++)
{
timestamp = (ev->time.tv_sec * 1000) + (ev->time.tv_usec / 1000);
switch (ev->type)
{
case EV_KEY:
_device_process_key(dev, ev, timestamp);
break;
case EV_REL:
DBG("\tRelative Event");
break;
case EV_ABS:
DBG("\tAbsolute Event");
break;
case EV_SYN:
_device_process_flush(dev, timestamp);
break;
default:
break;
}
}
}
static Eina_Bool
_cb_device_data(void *data, Ecore_Fd_Handler *hdlr EINA_UNUSED)
{
Ecore_Drm_Evdev *edev;
struct input_event ev[32];
int len = 0;
if (!(edev = data)) return EINA_TRUE;
do
{
len = read(edev->fd, &ev, sizeof(ev));
if ((len < 0) || ((len % sizeof(ev[0])) != 0))
{
if ((len < 0) && (errno != EAGAIN) && (errno != EINTR))
{
ERR("Device Died");
}
return EINA_TRUE;
}
edev->event_process(edev, ev, (len / sizeof(ev[0])));
} while (len > 0);
return EINA_TRUE;
}
/* external functions */
Ecore_Drm_Evdev *
_ecore_drm_evdev_device_create(Ecore_Drm_Seat *seat, const char *path, int fd)
{
Ecore_Drm_Evdev *edev;
char name[256] = "unknown";
if (!(edev = calloc(1, sizeof(Ecore_Drm_Evdev))))
return NULL;
edev->seat = seat;
edev->path = eina_stringshare_add(path);
edev->fd = fd;
if (ioctl(edev->fd, EVIOCGNAME(sizeof(name)), name) < 0)
DBG("Error getting device name: %m");
name[sizeof(name) - 1] = '\0';
edev->name = eina_stringshare_add(name);
if (!_device_handle(edev))
{
ERR("Unhandled Input Device: %s", name);
_ecore_drm_evdev_device_destroy(edev);
return NULL;
}
if (!_device_configure(edev))
{
_ecore_drm_evdev_device_destroy(edev);
return NULL;
}
edev->event_process = _device_process;
edev->hdlr =
ecore_main_fd_handler_add(edev->fd, ECORE_FD_READ,
_cb_device_data, edev, NULL, NULL);
if (!edev->hdlr)
{
ERR("Could not create fd handler");
_ecore_drm_evdev_device_destroy(edev);
return NULL;
}
return edev;
}
void
_ecore_drm_evdev_device_destroy(Ecore_Drm_Evdev *dev)
{
if (!dev) return;
if (dev->path) eina_stringshare_del(dev->path);
if (dev->name) eina_stringshare_del(dev->name);
if (dev->hdlr) ecore_main_fd_handler_del(dev->hdlr);
free(dev);
}

View File

@ -0,0 +1,152 @@
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include "ecore_drm_private.h"
/**
* @defgroup Ecore_Drm_Fb_Group
*
*/
/* TODO: DOXY !! */
EAPI Ecore_Drm_Fb *
ecore_drm_fb_create(Ecore_Drm_Device *dev, int width, int height)
{
Ecore_Drm_Fb *fb;
struct drm_mode_create_dumb carg;
struct drm_mode_destroy_dumb darg;
struct drm_mode_map_dumb marg;
if (!(fb = calloc(1, sizeof(Ecore_Drm_Fb)))) return NULL;
memset(&carg, 0, sizeof(struct drm_mode_create_dumb));
carg.bpp = 32; // FIXME: Hard-coded depth
carg.width = width;
carg.height = height;
if (drmIoctl(dev->drm.fd, DRM_IOCTL_MODE_CREATE_DUMB, &carg))
{
ERR("Could not create dumb framebuffer: %m");
goto create_err;
}
fb->from_client = EINA_TRUE;
fb->hdl = carg.handle;
fb->stride = carg.pitch;
fb->size = carg.size;
fb->fd = dev->drm.fd;
if (drmModeAddFB(dev->drm.fd, width, height, 24, 32,
fb->stride, fb->hdl, &fb->id))
{
ERR("Could not add framebuffer: %m");
goto add_err;
}
memset(&marg, 0, sizeof(struct drm_mode_map_dumb));
marg.handle = fb->hdl;
if (drmIoctl(dev->drm.fd, DRM_IOCTL_MODE_MAP_DUMB, &marg))
{
ERR("Could not map framebuffer: %m");
goto map_err;
}
fb->mmap =
mmap(0, fb->size, PROT_WRITE | PROT_READ, MAP_SHARED,
dev->drm.fd, marg.offset);
if (fb->mmap == MAP_FAILED)
{
ERR("Could not mmap framebuffer space: %m");
goto map_err;
}
memset(fb->mmap, 0, fb->size);
return fb;
map_err:
drmModeRmFB(fb->fd, fb->id);
add_err:
memset(&darg, 0, sizeof(struct drm_mode_destroy_dumb));
darg.handle = fb->hdl;
drmIoctl(dev->drm.fd, DRM_IOCTL_MODE_DESTROY_DUMB, &darg);
create_err:
free(fb);
return NULL;
}
EAPI void
ecore_drm_fb_destroy(Ecore_Drm_Fb *fb)
{
struct drm_mode_destroy_dumb darg;
if ((!fb) || (!fb->mmap)) return;
if (fb->id) drmModeRmFB(fb->fd, fb->id);
munmap(fb->mmap, fb->size);
memset(&darg, 0, sizeof(struct drm_mode_destroy_dumb));
darg.handle = fb->hdl;
drmIoctl(fb->fd, DRM_IOCTL_MODE_DESTROY_DUMB, &darg);
free(fb);
}
#ifdef HAVE_GBM
static void
_ecore_drm_fb_user_data_destroy(struct gbm_bo *bo EINA_UNUSED, void *data)
{
Ecore_Drm_Fb *fb;
if (!(fb = data)) return;
ecore_drm_fb_destroy(fb);
/* free(data); */
}
Ecore_Drm_Fb *
_ecore_drm_fb_bo_get(Ecore_Drm_Device *dev, struct gbm_bo *bo)
{
Ecore_Drm_Fb *fb;
unsigned int width, height;
unsigned int h[4], p[4], o[4];
int ret = -1;
if ((fb = gbm_bo_get_user_data(bo))) return fb;
if (!(fb = calloc(1, sizeof(Ecore_Drm_Fb)))) return NULL;
fb->bo = bo;
width = gbm_bo_get_width(bo);
height = gbm_bo_get_height(bo);
fb->stride = gbm_bo_get_stride(bo);
fb->hdl = gbm_bo_get_handle(bo).u32;
fb->size = (fb->stride * height);
fb->fd = dev->drm.fd;
h[0] = fb->hdl;
p[0] = fb->stride;
o[0] = 0;
ret = drmModeAddFB2(dev->drm.fd, width, height, dev->format, h, p, o,
&fb->id, 0);
if (ret)
{
ret = drmModeAddFB(dev->drm.fd, width, height, 24, 32,
fb->stride, fb->hdl, &fb->id);
}
if (ret)
{
ERR("Error during ModeAddFb");
free(fb);
return NULL;
}
gbm_bo_set_user_data(bo, fb, _ecore_drm_fb_user_data_destroy);
return fb;
}
#endif

View File

@ -0,0 +1,303 @@
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include "ecore_drm_private.h"
/* local functions */
static Ecore_Drm_Seat *
_seat_get(Ecore_Drm_Input *input, const char *seat)
{
Ecore_Drm_Seat *s;
Eina_List *l;
EINA_LIST_FOREACH(input->dev->seats, l, s)
{
if (!strcmp(s->name, seat)) return s;
}
if (!(s = calloc(1, sizeof(Ecore_Drm_Seat))))
return NULL;
s->input = input;
s->name = eina_stringshare_add(seat);
input->dev->seats = eina_list_append(input->dev->seats, s);
return s;
}
static Eina_Bool
_device_add(Ecore_Drm_Input *input, struct udev_device *device)
{
Ecore_Drm_Evdev *edev;
Ecore_Drm_Seat *seat;
const char *dev_seat, *wl_seat;
const char *node;
char n[PATH_MAX];
int fd = -1;
if (!(dev_seat = udev_device_get_property_value(device, "ID_SEAT")))
dev_seat = "seat0";
if (strcmp(dev_seat, input->seat)) return EINA_FALSE;
if (!(wl_seat = udev_device_get_property_value(device, "WL_SEAT")))
wl_seat = "seat0";
if (!(seat = _seat_get(input, wl_seat)))
return EINA_FALSE;
node = udev_device_get_devnode(device);
strcpy(n, node);
fd = open(n, O_RDWR | O_NONBLOCK);
/* _ecore_drm_message_send(ECORE_DRM_OP_DEVICE_OPEN, -1, n, strlen(n)); */
/* _ecore_drm_message_receive(ECORE_DRM_OP_DEVICE_OPEN, &fd, NULL, 0); */
/* DBG("Opened Restricted Input: %s %d", node, fd); */
if (!(edev = _ecore_drm_evdev_device_create(seat, node, fd)))
{
close(fd);
/* _ecore_drm_message_send(ECORE_DRM_OP_DEVICE_CLOSE, fd, NULL, 0); */
/* _ecore_drm_message_receive(ECORE_DRM_OP_DEVICE_OPEN, &fd, NULL, 0); */
return EINA_FALSE;
}
seat->devices = eina_list_append(seat->devices, edev);
/* TODO: finish */
return EINA_TRUE;
}
static void
_device_remove(Ecore_Drm_Input *input, const char *device)
{
Ecore_Drm_Seat *seat;
Eina_List *l;
if (!input) return;
EINA_LIST_FOREACH(input->dev->seats, l, seat)
{
Ecore_Drm_Evdev *edev;
Eina_List *ll;
EINA_LIST_FOREACH(seat->devices, ll, edev)
{
if (!strcmp(edev->path, device))
{
seat->devices = eina_list_remove(seat->devices, edev);
_ecore_drm_evdev_device_destroy(edev);
break;
}
}
}
}
static Eina_Bool
_cb_input_event(void *data, Ecore_Fd_Handler *hdlr EINA_UNUSED)
{
Ecore_Drm_Input *input;
struct udev_device *udevice;
const char *act;
DBG("Input Event");
if (!(input = data)) return EINA_FALSE;
if (!(udevice = udev_monitor_receive_device(input->monitor)))
return EINA_TRUE;
if (!(act = udev_device_get_action(udevice))) return EINA_TRUE;
if (strncmp("event", udev_device_get_sysname(udevice), 5) != 0)
goto err;
if (!strcmp(act, "add"))
{
DBG("\tDevice Added");
_device_add(input, udevice);
}
else if (!strcmp(act, "remove"))
{
const char *node;
node = udev_device_get_devnode(udevice);
DBG("\tDevice Removed: %s", node);
_device_remove(input, node);
}
return EINA_TRUE;
err:
if (udevice) udev_device_unref(udevice);
return EINA_TRUE;
}
static Eina_Bool
_devices_add(Ecore_Drm_Input *input)
{
struct udev_enumerate *uenum;
struct udev_list_entry *uentry;
struct udev_device *udevice;
const char *path, *name;
Eina_Bool found = EINA_FALSE;
uenum = udev_enumerate_new(udev);
udev_enumerate_add_match_subsystem(uenum, "input");
udev_enumerate_scan_devices(uenum);
udev_list_entry_foreach(uentry, udev_enumerate_get_list_entry(uenum))
{
path = udev_list_entry_get_name(uentry);
udevice = udev_device_new_from_syspath(udev, path);
name = udev_device_get_sysname(udevice);
if (strncmp("event", name, 5) != 0)
{
udev_device_unref(udevice);
continue;
}
if (!_device_add(input, udevice))
{
udev_device_unref(udevice);
continue;
}
found = EINA_TRUE;
udev_device_unref(udevice);
}
udev_enumerate_unref(uenum);
if (!found)
{
ERR("No Input Devices Found");
return EINA_FALSE;
}
return EINA_TRUE;
}
/* public functions */
EAPI Eina_Bool
ecore_drm_inputs_create(Ecore_Drm_Device *dev)
{
Ecore_Drm_Input *input;
/* check for valid device */
if ((!dev) || (!udev)) return EINA_FALSE;
/* try to allocate space for input structure */
if (!(input = calloc(1, sizeof(Ecore_Drm_Input))))
return EINA_FALSE;
/* FIXME: Hardcoded seat name */
input->seat = eina_stringshare_add("seat0");
input->dev = dev;
/* try to enable this input */
if (!ecore_drm_inputs_enable(input))
{
ERR("Could not enable input");
if (input->seat) eina_stringshare_del(input->seat);
free(input);
return EINA_FALSE;
}
/* add this input to dev */
dev->inputs = eina_list_append(dev->inputs, input);
return EINA_TRUE;
}
EAPI void
ecore_drm_inputs_destroy(Ecore_Drm_Device *dev)
{
Ecore_Drm_Seat *seat;
Eina_List *l;
if (!dev) return;
EINA_LIST_FOREACH(dev->seats, l, seat)
{
Ecore_Drm_Evdev *edev;
EINA_LIST_FREE(seat->devices, edev)
_ecore_drm_evdev_device_destroy(edev);
}
}
EAPI Eina_Bool
ecore_drm_inputs_enable(Ecore_Drm_Input *input)
{
/* check for valid input */
if (!input) return EINA_FALSE;
if (!input->monitor)
input->monitor = udev_monitor_new_from_netlink(udev, "udev");
if (!input->monitor)
{
ERR("Could not create udev monitor: %m");
return EINA_FALSE;
}
/* setup input filter */
udev_monitor_filter_add_match_subsystem_devtype(input->monitor,
"input", NULL);
/* try to enable receiving udev events */
if (udev_monitor_enable_receiving(input->monitor))
{
ERR("Could not bind udev monitor: %m");
udev_monitor_unref(input->monitor);
return EINA_FALSE;
}
/* save the fd */
if ((input->fd = udev_monitor_get_fd(input->monitor)) < 0)
{
ERR("Input monitor has no fd: %m");
udev_monitor_unref(input->monitor);
return EINA_FALSE;
}
/* create fd handler */
if (!input->hdlr)
{
input->hdlr =
ecore_main_fd_handler_add(input->fd, ECORE_FD_READ,
_cb_input_event, input, NULL, NULL);
}
if (!input->hdlr)
{
ERR("Failed to setup input fd handler: %m");
udev_monitor_unref(input->monitor);
return EINA_FALSE;
}
/* try to add devices */
if (!_devices_add(input))
{
ERR("Could not add input devices");
udev_monitor_unref(input->monitor);
return EINA_FALSE;
}
input->enabled = EINA_TRUE;
input->suspended = EINA_FALSE;
return EINA_TRUE;
}
EAPI void
ecore_drm_inputs_disable(Ecore_Drm_Input *input)
{
if (!input) return;
}

View File

@ -0,0 +1,712 @@
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "ecore_drm_private.h"
#define ALEN(array) (sizeof(array) / sizeof(array)[0])
static const char *conn_types[] =
{
"None", "VGA", "DVI", "DVI", "DVI",
"Composite", "TV", "LVDS", "CTV", "DIN",
"DP", "HDMI", "HDMI", "TV", "eDP",
};
/* local functions */
#ifdef HAVE_GBM
static Eina_Bool
_ecore_drm_output_context_create(Ecore_Drm_Device *dev, EGLSurface surface)
{
EGLBoolean r;
static const EGLint attribs[] =
{
EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE
};
if ((!dev->egl.disp) || (!dev->egl.cfg)) return EINA_FALSE;
if (!eglBindAPI(EGL_OPENGL_ES_API))
{
ERR("Could not bind egl api");
return EINA_FALSE;
}
dev->egl.ctxt =
eglCreateContext(dev->egl.disp, dev->egl.cfg, EGL_NO_CONTEXT, attribs);
if (!dev->egl.ctxt)
{
ERR("Could not create Egl Context");
return EINA_FALSE;
}
r = eglMakeCurrent(dev->egl.disp, surface, surface, dev->egl.ctxt);
if (r == EGL_FALSE)
{
ERR("Could not make surface current");
return EINA_FALSE;
}
/* TODO: bind display, handle extensions, compile shaders, etc */
return EINA_TRUE;
}
static Eina_Bool
_ecore_drm_output_hardware_setup(Ecore_Drm_Device *dev, Ecore_Drm_Output *output)
{
unsigned int i = 0;
int flags = 0;
int w = 0, h = 0;
if ((!dev) || (!output)) return EINA_FALSE;
if (output->current_mode)
{
w = output->current_mode->width;
h = output->current_mode->height;
}
else
{
w = 1024;
h = 768;
}
flags = (GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING);
/* FIXME: NB: Should this really be XARGB8888 ?? */
if (!(output->surface =
gbm_surface_create(dev->gbm, w, h, GBM_FORMAT_ARGB8888, flags)))
{
ERR("Could not create output surface");
return EINA_FALSE;
}
if (!(output->egl.surface =
eglCreateWindowSurface(dev->egl.disp, dev->egl.cfg,
output->surface, NULL)))
{
ERR("Could not create output egl surface");
gbm_surface_destroy(output->surface);
return EINA_FALSE;
}
if (!dev->egl.ctxt)
{
if (!_ecore_drm_output_context_create(dev, output->egl.surface))
{
ERR("Could not create context");
gbm_surface_destroy(output->surface);
return EINA_FALSE;
}
}
flags = (GBM_BO_USE_CURSOR_64X64 | GBM_BO_USE_WRITE);
for (i = 0; i < NUM_FRAME_BUFFERS; i++)
{
if (output->cursor[i]) continue;
if (!(output->cursor[i] =
gbm_bo_create(dev->gbm, 64, 64, dev->format, flags)))
{
continue;
}
}
if ((!output->cursor[0]) || (!output->cursor[1]))
{
WRN("Hardware Cursor Buffers not available");
dev->cursors_broken = EINA_TRUE;
}
return EINA_TRUE;
}
static void
_ecore_drm_output_hardware_render(Ecore_Drm_Output *output)
{
struct gbm_bo *bo;
int ret;
if (!output) return;
if (!output->current_mode) return;
glViewport(output->x, output->y,
output->current_mode->width, output->current_mode->height);
if (eglMakeCurrent(output->dev->egl.disp, output->egl.surface,
output->egl.surface, output->dev->egl.ctxt) == EGL_FALSE)
return;
/* TODO: calculate damage */
/* TODO: repaint surfaces */
/* TODO: call egl functions to repaint */
glClearColor(1.0, 1.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
glFlush();
/* eglSwapInterval(output->dev->egl.disp, 1); */
eglSwapBuffers(output->dev->egl.disp, output->egl.surface);
if (!(bo = gbm_surface_lock_front_buffer(output->surface)))
{
ERR("Failed to lock front buffer");
return;
}
if (!(output->next = _ecore_drm_fb_bo_get(output->dev, bo)))
{
ERR("Failed to get FB from bo");
gbm_surface_release_buffer(output->surface, bo);
}
}
#endif
static Eina_Bool
_ecore_drm_output_software_setup(Ecore_Drm_Device *dev, Ecore_Drm_Output *output)
{
unsigned int i = 0;
int w = 0, h = 0;
if ((!dev) || (!output)) return EINA_FALSE;
if (output->current_mode)
{
w = output->current_mode->width;
h = output->current_mode->height;
}
else
{
w = 1024;
h = 768;
}
for (i = 0; i < NUM_FRAME_BUFFERS; i++)
{
if (!(output->dumb[i] = ecore_drm_fb_create(dev, w, h)))
{
ERR("Could not create dumb framebuffer %d", i);
goto err;
}
}
return EINA_TRUE;
err:
for (i = 0; i < NUM_FRAME_BUFFERS; i++)
{
if (output->dumb[i]) ecore_drm_fb_destroy(output->dumb[i]);
output->dumb[i] = NULL;
}
return EINA_FALSE;
}
static void
_ecore_drm_output_software_render(Ecore_Drm_Output *output)
{
if (!output) return;
if (!output->current_mode) return;
}
static int
_ecore_drm_output_crtc_find(Ecore_Drm_Device *dev, drmModeRes *res, drmModeConnector *conn)
{
drmModeEncoder *enc;
unsigned int p;
int i = 0, j = 0;
for (j = 0; j < conn->count_encoders; j++)
{
/* get the encoder on this connector */
if (!(enc = drmModeGetEncoder(dev->drm.fd, conn->encoders[j])))
{
ERR("Failed to get encoder: %m");
return -1;
}
p = enc->possible_crtcs;
drmModeFreeEncoder(enc);
for (i = 0; i < res->count_crtcs; i++)
{
if ((p & (1 << i)) &&
(!(dev->crtc_allocator & (1 << res->crtcs[i]))))
{
return i;
}
}
}
return -1;
}
static Ecore_Drm_Output_Mode *
_ecore_drm_output_mode_add(Ecore_Drm_Output *output, drmModeModeInfo *info)
{
Ecore_Drm_Output_Mode *mode;
uint64_t refresh;
/* try to allocate space for mode */
if (!(mode = malloc(sizeof(Ecore_Drm_Output_Mode))))
{
ERR("Could not allocate space for mode");
return NULL;
}
mode->flags = 0;
mode->width = info->hdisplay;
mode->height = info->vdisplay;
refresh = (info->clock * 1000000LL / info->htotal + info->vtotal / 2) / info->vtotal;
if (info->flags & DRM_MODE_FLAG_INTERLACE)
refresh *= 2;
if (info->flags & DRM_MODE_FLAG_DBLSCAN)
refresh /= 2;
if (info->vscan > 1)
refresh /= info->vscan;
mode->refresh = refresh;
mode->info = *info;
/* DBG("Added Mode: %dx%d@%d to Output %d", */
/* mode->width, mode->height, mode->refresh, output->crtc_id); */
output->modes = eina_list_append(output->modes, mode);
return mode;
}
static Ecore_Drm_Output *
_ecore_drm_output_create(Ecore_Drm_Device *dev, drmModeRes *res, drmModeConnector *conn, int x, int y)
{
Ecore_Drm_Output *output;
Ecore_Drm_Output_Mode *mode;
const char *conn_name;
char name[32];
int i = 0;
drmModeEncoder *enc;
drmModeModeInfo crtc_mode;
drmModeCrtc *crtc;
Eina_List *l;
i = _ecore_drm_output_crtc_find(dev, res, conn);
if (i < 0)
{
ERR("Could not find crtc or encoder for connector");
return NULL;
}
/* try to allocate space for output */
if (!(output = calloc(1, sizeof(Ecore_Drm_Output))))
{
ERR("Could not allocate space for output");
return NULL;
}
output->dev = dev;
output->x = x;
output->y = y;
output->subpixel = conn->subpixel;
output->make = eina_stringshare_add("unknown");
output->model = eina_stringshare_add("unknown");
if (conn->connector_type < ALEN(conn_types))
conn_name = conn_types[conn->connector_type];
else
conn_name = "UNKNOWN";
snprintf(name, sizeof(name), "%s%d", conn_name, conn->connector_type_id);
output->name = eina_stringshare_add(name);
output->crtc_id = res->crtcs[i];
dev->crtc_allocator |= (1 << output->crtc_id);
output->conn_id = conn->connector_id;
output->crtc = drmModeGetCrtc(dev->drm.fd, output->crtc_id);
memset(&mode, 0, sizeof(mode));
if ((enc = drmModeGetEncoder(dev->drm.fd, conn->encoder_id)))
{
crtc = drmModeGetCrtc(dev->drm.fd, enc->crtc_id);
drmModeFreeEncoder(enc);
if (!crtc) goto mode_err;
if (crtc->mode_valid) crtc_mode = crtc->mode;
drmModeFreeCrtc(crtc);
}
for (i = 0; i < conn->count_modes; i++)
{
if (!(mode = _ecore_drm_output_mode_add(output, &conn->modes[i])))
{
ERR("Failed to add mode to output");
goto mode_err;
}
}
EINA_LIST_REVERSE_FOREACH(output->modes, l, mode)
{
if (!memcmp(&crtc_mode, &mode->info, sizeof(crtc_mode)))
{
output->current_mode = mode;
break;
}
}
if (!output->current_mode)
output->current_mode = _ecore_drm_output_mode_add(output, &crtc_mode);
#ifdef HAVE_GBM
if ((dev->use_hw_accel) && (dev->gbm))
{
if (!_ecore_drm_output_hardware_setup(dev, output))
{
ERR("Could not setup output for hardware acceleration");
dev->use_hw_accel = EINA_FALSE;
if (!_ecore_drm_output_software_setup(dev, output))
goto mode_err;
else
DBG("Setup Output %d for Software Rendering", output->crtc_id);
}
else
DBG("Setup Output %d for Hardware Acceleration", output->crtc_id);
}
else
#endif
{
dev->use_hw_accel = EINA_FALSE;
if (!_ecore_drm_output_software_setup(dev, output))
goto mode_err;
else
DBG("Setup Output %d for Software Rendering", output->crtc_id);
}
/* TODO: output_init_pixman/output_init_egl ?? */
return output;
mode_err:
EINA_LIST_FREE(output->modes, mode)
free(mode);
drmModeFreeCrtc(output->crtc);
dev->crtc_allocator &= ~(1 << output->crtc_id);
free(output);
return NULL;
}
void
_ecore_drm_output_frame_finish(Ecore_Drm_Output *output)
{
if (!output) return;
if (output->need_repaint) ecore_drm_output_repaint(output);
output->repaint_scheduled = EINA_FALSE;
}
void
_ecore_drm_output_fb_release(Ecore_Drm_Output *output, Ecore_Drm_Fb *fb)
{
if ((!output) || (!fb)) return;
if ((fb->mmap) && (fb != output->dumb[0]) && (fb != output->dumb[1]))
ecore_drm_fb_destroy(fb);
/* #ifdef HAVE_GBM */
/* else if (fb->bo) */
/* gbm_bo_destroy(fb->bo); */
/* #endif */
}
void
_ecore_drm_output_repaint_start(Ecore_Drm_Output *output)
{
unsigned int fb;
DBG("Output Repaint Start");
if (!output) return;
if (!output->current)
{
DBG("\tNo Current FB");
goto finish;
}
fb = output->current->id;
if (drmModePageFlip(output->dev->drm.fd, output->crtc_id, fb,
DRM_MODE_PAGE_FLIP_EVENT, output) < 0)
{
ERR("Could not schedule output page flip event");
goto finish;
}
return;
finish:
_ecore_drm_output_frame_finish(output);
}
/**
* @defgroup Ecore_Drm_Output_Group
*
*/
/* TODO: DOXY !! */
/* public functions */
EAPI Eina_Bool
ecore_drm_outputs_create(Ecore_Drm_Device *dev)
{
Eina_Bool ret = EINA_TRUE;
Ecore_Drm_Output *output;
drmModeConnector *conn;
drmModeRes *res;
drmModeCrtc *crtc;
int i = 0, x = 0, y = 0;
/* DBG("Create outputs for %d", dev->drm.fd); */
/* get the resources */
if (!(res = drmModeGetResources(dev->drm.fd)))
{
ERR("Could not get resources for drm card: %m");
return EINA_FALSE;
}
if (!(dev->crtcs = calloc(res->count_crtcs, sizeof(unsigned int))))
{
ERR("Could not allocate space for crtcs");
/* free resources */
drmModeFreeResources(res);
return EINA_FALSE;
}
dev->crtc_count = res->count_crtcs;
memcpy(dev->crtcs, res->crtcs, sizeof(unsigned int) * res->count_crtcs);
dev->min_width = res->min_width;
dev->min_height = res->min_height;
dev->max_width = res->max_width;
dev->max_height = res->max_height;
for (i = 0; i < res->count_connectors; i++)
{
if (i > 0) break;
/* get the connector */
if (!(conn = drmModeGetConnector(dev->drm.fd, res->connectors[i])))
continue;
if ((conn->connection == DRM_MODE_CONNECTED) &&
(conn->count_modes > 0))
{
drmModeEncoder *enc;
/* create output for this connector */
if (!(output = _ecore_drm_output_create(dev, res, conn, x, y)))
{
/* free the connector */
drmModeFreeConnector(conn);
continue;
}
output->drm_fd = dev->drm.fd;
dev->outputs = eina_list_append(dev->outputs, output);
if (!(enc = drmModeGetEncoder(dev->drm.fd, conn->encoder_id)))
{
drmModeFreeConnector(conn);
continue;
}
if (!(crtc = drmModeGetCrtc(dev->drm.fd, enc->crtc_id)))
{
drmModeFreeEncoder(enc);
drmModeFreeConnector(conn);
continue;
}
x += crtc->width;
drmModeFreeCrtc(crtc);
drmModeFreeEncoder(enc);
}
/* free the connector */
drmModeFreeConnector(conn);
}
ret = EINA_TRUE;
if (eina_list_count(dev->outputs) < 1)
{
ret = EINA_FALSE;
free(dev->crtcs);
}
/* free resources */
drmModeFreeResources(res);
/* TODO: add hook for udev drm output updates */
return ret;
}
EAPI void
ecore_drm_output_free(Ecore_Drm_Output *output)
{
Ecore_Drm_Output_Mode *mode;
/* check for valid output */
if (!output) return;
/* free modes */
EINA_LIST_FREE(output->modes, mode)
free(mode);
/* free strings */
if (output->name) eina_stringshare_del(output->name);
if (output->model) eina_stringshare_del(output->model);
if (output->make) eina_stringshare_del(output->make);
free(output);
}
EAPI void
ecore_drm_output_cursor_size_set(Ecore_Drm_Output *output, int handle, int w, int h)
{
if (!output) return;
drmModeSetCursor(output->drm_fd, output->crtc_id, handle, w, h);
}
EAPI Eina_Bool
ecore_drm_output_enable(Ecore_Drm_Output *output)
{
Ecore_Drm_Output_Mode *mode;
if ((!output) || (!output->current)) return EINA_FALSE;
mode = output->current_mode;
if (drmModeSetCrtc(output->drm_fd, output->crtc_id, output->current->id,
0, 0, &output->conn_id, 1, &mode->info) < 0)
{
ERR("Could not set output crtc: %m");
return EINA_FALSE;
}
return EINA_TRUE;
}
EAPI void
ecore_drm_output_fb_release(Ecore_Drm_Output *output, Ecore_Drm_Fb *fb)
{
if ((!output) || (!fb)) return;
if ((fb->mmap) && (fb != output->dumb[0]) && (fb != output->dumb[1]))
ecore_drm_fb_destroy(fb);
#ifdef HAVE_GBM
else if (fb->bo)
{
if (fb->from_client)
gbm_bo_destroy(fb->bo);
else
gbm_surface_release_buffer(output->surface, fb->bo);
}
#endif
}
EAPI void
ecore_drm_output_repaint(Ecore_Drm_Output *output)
{
Eina_List *l;
Ecore_Drm_Sprite *sprite;
int ret = 0;
if (!output) return;
DBG("Output Repaint: %d %d", output->crtc_id, output->conn_id);
/* TODO: assign planes ? */
if (!output->next)
{
#ifdef HAVE_GBM
if (output->dev->use_hw_accel)
{
_ecore_drm_output_hardware_render(output);
}
else
#endif
{
_ecore_drm_output_software_render(output);
}
}
if (!output->next)
{
DBG("\tNo Next Fb");
return;
}
output->need_repaint = EINA_FALSE;
if (!output->current)
{
Ecore_Drm_Output_Mode *mode;
mode = output->current_mode;
ret = drmModeSetCrtc(output->dev->drm.fd, output->crtc_id,
output->next->id, 0, 0, &output->conn_id, 1,
&mode->info);
if (ret)
{
ERR("Setting output mode failed");
goto err;
}
}
if (drmModePageFlip(output->dev->drm.fd, output->crtc_id, output->next->id,
DRM_MODE_PAGE_FLIP_EVENT, output) < 0)
{
ERR("Scheduling pageflip failed");
goto err;
}
output->pending_flip = EINA_TRUE;
EINA_LIST_FOREACH(output->dev->sprites, l, sprite)
{
unsigned int flags = 0, id = 0;
drmVBlank vbl =
{
.request.type = (DRM_VBLANK_RELATIVE | DRM_VBLANK_EVENT),
.request.sequence = 1,
};
if (((!sprite->current_fb) && (!sprite->next_fb)) ||
(!ecore_drm_sprites_crtc_supported(output, sprite->crtcs)))
continue;
if ((sprite->next_fb) && (!output->dev->cursors_broken))
id = sprite->next_fb->id;
ecore_drm_sprites_fb_set(sprite, id, flags);
vbl.request.signal = (unsigned long)sprite;
ret = drmWaitVBlank(output->dev->drm.fd, &vbl);
if (ret) ERR("Error Wait VBlank: %m");
sprite->output = output;
output->pending_vblank = EINA_TRUE;
}
return;
err:
if (output->next)
{
ecore_drm_output_fb_release(output, output->next);
output->next = NULL;
}
}

View File

@ -0,0 +1,283 @@
#ifndef _ECORE_DRM_PRIVATE_H
# define _ECORE_DRM_PRIVATE_H
# include "Ecore.h"
# include "ecore_private.h"
# include "Ecore_Input.h"
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <unistd.h>
# include <errno.h>
# include <sys/mman.h>
# include <fcntl.h>
# include <libudev.h>
# include <linux/input.h>
//# include <libinput.h>
# include <xf86drm.h>
# include <xf86drmMode.h>
# include <drm_fourcc.h>
# ifdef HAVE_GBM
# include <gbm.h>
# include <EGL/egl.h>
# include <EGL/eglext.h>
# include <GLES2/gl2.h>
# include <GLES2/gl2ext.h>
# endif
# include <Ecore_Drm.h>
# define NUM_FRAME_BUFFERS 2
# ifndef DRM_MAJOR
# define DRM_MAJOR 226
# endif
# ifndef DRM_CAP_TIMESTAMP_MONOTONIC
# define DRM_CAP_TIMESTAMP_MONOTONIC 0x6
# endif
# ifdef ECORE_DRM_DEFAULT_LOG_COLOR
# undef ECORE_DRM_DEFAULT_LOG_COLOR
# endif
# define ECORE_DRM_DEFAULT_LOG_COLOR EINA_COLOR_BLUE
# define EVDEV_SEAT_POINTER (1 << 0)
# define EVDEV_SEAT_KEYBOARD (1 << 1)
# define EVDEV_SEAT_TOUCH (1 << 2)
# ifdef ERR
# undef ERR
# endif
# ifdef DBG
# undef DBG
# endif
# ifdef INF
# undef INF
# endif
# ifdef WRN
# undef WRN
# endif
# ifdef CRIT
# undef CRIT
# endif
extern int _ecore_drm_log_dom;
/* undef this for non-testing builds */
# define LOG_TO_FILE
# ifdef LOG_TO_FILE
extern FILE *lg;
# define ERR(...) \
EINA_LOG_DOM_ERR(_ecore_drm_log_dom, __VA_ARGS__); \
fflush(lg);
# define DBG(...) \
EINA_LOG_DOM_DBG(_ecore_drm_log_dom, __VA_ARGS__); \
fflush(lg);
# define INF(...) \
EINA_LOG_DOM_INFO(_ecore_drm_log_dom, __VA_ARGS__); \
fflush(lg);
# define WRN(...) \
EINA_LOG_DOM_WARN(_ecore_drm_log_dom, __VA_ARGS__); \
fflush(lg);
# define CRIT(...) \
EINA_LOG_DOM_CRIT(_ecore_drm_log_dom, __VA_ARGS__); \
fflush(lg);
# else
# define ERR(...) EINA_LOG_DOM_ERR(_ecore_drm_log_dom, __VA_ARGS__)
# define DBG(...) EINA_LOG_DOM_DBG(_ecore_drm_log_dom, __VA_ARGS__)
# define INF(...) EINA_LOG_DOM_INFO(_ecore_drm_log_dom, __VA_ARGS__)
# define WRN(...) EINA_LOG_DOM_WARN(_ecore_drm_log_dom, __VA_ARGS__)
# define CRIT(...) EINA_LOG_DOM_CRIT(_ecore_drm_log_dom, __VA_ARGS__)
# endif
extern struct udev *udev;
struct _Ecore_Drm_Output_Mode
{
unsigned int flags;
int width, height;
unsigned int refresh;
drmModeModeInfo info;
};
struct _Ecore_Drm_Output
{
Ecore_Drm_Device *dev;
unsigned int crtc_id;
unsigned int conn_id;
drmModeCrtcPtr crtc;
int x, y;
int drm_fd;
Eina_Bool need_repaint : 1;
Eina_Bool repaint_scheduled : 1;
Eina_Bool pending_flip : 1;
Eina_Bool pending_vblank : 1;
const char *make, *model, *name;
unsigned int subpixel;
Ecore_Drm_Output_Mode *current_mode;
Eina_List *modes;
Ecore_Drm_Fb *current, *next;
Ecore_Drm_Fb *dumb[NUM_FRAME_BUFFERS];
# ifdef HAVE_GBM
struct gbm_surface *surface;
struct gbm_bo *cursor[NUM_FRAME_BUFFERS];
struct
{
EGLSurface surface;
} egl;
# endif
/* TODO: finish */
};
struct _Ecore_Drm_Seat
{
// struct libinput_seat *seat;
const char *name;
Ecore_Drm_Input *input;
Eina_List *devices;
};
struct _Ecore_Drm_Input
{
int fd;
const char *seat;
struct udev_monitor *monitor;
Ecore_Fd_Handler *hdlr;
Ecore_Drm_Device *dev;
Eina_Bool enabled : 1;
Eina_Bool suspended : 1;
};
struct _Ecore_Drm_Evdev
{
Ecore_Drm_Seat *seat;
/* struct libinput *linput; */
/* struct libinput_device *dev; */
const char *name, *path;
int fd;
struct
{
int min_x, min_y;
int max_x, max_y;
int x, y;
} abs;
Ecore_Drm_Evdev_Event_Type pending_event;
Ecore_Drm_Evdev_Capabilities caps;
Ecore_Drm_Seat_Capabilities seat_caps;
void (*event_process)(Ecore_Drm_Evdev *dev, struct input_event *event, int count);
Ecore_Fd_Handler *hdlr;
};
struct _Ecore_Drm_Sprite
{
Ecore_Drm_Fb *current_fb, *next_fb;
Ecore_Drm_Output *output;
int drm_fd;
unsigned int crtcs;
unsigned int plane_id;
struct
{
int x, y;
unsigned int w, h;
} src, dest;
unsigned int num_formats;
unsigned int formats[];
};
struct _Ecore_Drm_Device
{
int id;
const char *seat;
struct
{
int fd;
const char *name;
const char *path;
clockid_t clock;
Ecore_Fd_Handler *hdlr;
Ecore_Idle_Enterer *idler;
} drm;
unsigned int min_width, min_height;
unsigned int max_width, max_height;
unsigned int crtc_count;
unsigned int *crtcs;
unsigned int crtc_allocator;
Eina_List *seats;
Eina_List *inputs;
Eina_List *outputs;
Eina_List *sprites;
struct
{
int fd;
const char *name;
Ecore_Event_Handler *event_hdlr;
} tty;
unsigned int format;
Eina_Bool use_hw_accel : 1;
Eina_Bool cursors_broken : 1;
#ifdef HAVE_GBM
struct gbm_device *gbm;
struct
{
EGLDisplay disp;
EGLContext ctxt;
EGLConfig cfg;
} egl;
#endif
};
void _ecore_drm_message_send(int opcode, int fd, void *data, size_t bytes);
Eina_Bool _ecore_drm_message_receive(int opcode, int *fd, void **data, size_t bytes);
Ecore_Drm_Evdev *_ecore_drm_evdev_device_create(Ecore_Drm_Seat *seat, const char *path, int fd);
void _ecore_drm_evdev_device_destroy(Ecore_Drm_Evdev *evdev);
/* int _ecore_drm_evdev_event_process(struct libinput_event *event); */
Ecore_Drm_Fb *_ecore_drm_fb_create(Ecore_Drm_Device *dev, int width, int height);
void _ecore_drm_fb_destroy(Ecore_Drm_Fb *fb);
#ifdef HAVE_GBM
Ecore_Drm_Fb *_ecore_drm_fb_bo_get(Ecore_Drm_Device *dev, struct gbm_bo *bo);
#endif
void _ecore_drm_output_fb_release(Ecore_Drm_Output *output, Ecore_Drm_Fb *fb);
void _ecore_drm_output_repaint_start(Ecore_Drm_Output *output);
void _ecore_drm_output_frame_finish(Ecore_Drm_Output *output);
#endif

View File

@ -0,0 +1,115 @@
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include "ecore_drm_private.h"
/**
* @defgroup Ecore_Drm_Fb_Group
*
*/
/* TODO: DOXY !! */
EAPI Eina_Bool
ecore_drm_sprites_create(Ecore_Drm_Device *dev)
{
drmModePlaneRes *res;
drmModePlane *p;
unsigned int i = 0;
/* check for valid device */
if ((!dev) || (dev->drm.fd < 0)) return EINA_FALSE;
/* get plane resources */
if (!(res = drmModeGetPlaneResources(dev->drm.fd))) return EINA_FALSE;
for (i = 0; i < res->count_planes; i++)
{
Ecore_Drm_Sprite *sprite;
if (!(p = drmModeGetPlane(dev->drm.fd, res->planes[i])))
continue;
/* allocate space for sprite */
if (!(sprite =
malloc(sizeof(Ecore_Drm_Sprite) +
((sizeof(unsigned int)) * p->count_formats))))
{
drmModeFreePlane(p);
continue;
}
sprite->drm_fd = dev->drm.fd;
sprite->crtcs = p->possible_crtcs;
sprite->plane_id = p->plane_id;
sprite->num_formats = p->count_formats;
memcpy(sprite->formats, p->formats,
p->count_formats * sizeof(p->formats[0]));
drmModeFreePlane(p);
dev->sprites = eina_list_append(dev->sprites, sprite);
}
/* free resources */
drmModeFreePlaneResources(res);
return EINA_TRUE;
}
EAPI void
ecore_drm_sprites_destroy(Ecore_Drm_Device *dev)
{
Ecore_Drm_Sprite *sprite;
/* check for valid device */
if (!dev) return;
EINA_LIST_FREE(dev->sprites, sprite)
{
ecore_drm_sprites_fb_set(sprite, 0, 0);
_ecore_drm_output_fb_release(sprite->output, sprite->current_fb);
_ecore_drm_output_fb_release(sprite->output, sprite->next_fb);
free(sprite);
}
}
EAPI void
ecore_drm_sprites_fb_set(Ecore_Drm_Sprite *sprite, int fb_id, int flags)
{
if ((!sprite) || (!sprite->output)) return;
if (fb_id)
{
drmModeSetPlane(sprite->drm_fd, sprite->plane_id,
sprite->output->crtc_id, fb_id, flags,
sprite->dest.x, sprite->dest.y, sprite->dest.w,
sprite->dest.h, sprite->src.x, sprite->src.y,
sprite->src.w, sprite->src.h);
}
else
{
drmModeSetPlane(sprite->drm_fd, sprite->plane_id,
sprite->output->crtc_id, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0);
}
}
EAPI Eina_Bool
ecore_drm_sprites_crtc_supported(Ecore_Drm_Output *output, unsigned int supported)
{
Ecore_Drm_Device *dev;
unsigned int c = 0;
dev = output->dev;
for (c = 0; c < dev->crtc_count; c++)
{
if (dev->crtcs[c] != output->crtc_id) continue;
if ((supported) && (1 << c)) return EINA_FALSE;
}
return EINA_TRUE;
}

View File

@ -0,0 +1,297 @@
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "ecore_drm_private.h"
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <linux/vt.h>
#include <linux/kd.h>
#ifndef KDSKBMUTE
# define KDSKBMUTE 0x4B51
#endif
static Eina_Bool
_ecore_drm_tty_cb_signal(void *data, int type EINA_UNUSED, void *event)
{
Ecore_Drm_Device *dev;
Ecore_Event_Signal_User *ev;
dev = data;
ev = event;
DBG("Caught user signal: %d", ev->number);
if (ev->number == 1)
{
Ecore_Drm_Input *input;
Ecore_Drm_Output *output;
Ecore_Drm_Sprite *sprite;
Eina_List *l;
DBG("Release VT");
/* disable inputs (suspends) */
EINA_LIST_FOREACH(dev->inputs, l, input)
ecore_drm_inputs_disable(input);
/* disable hardware cursor */
EINA_LIST_FOREACH(dev->outputs, l, output)
ecore_drm_output_cursor_size_set(output, 0, 0, 0);
/* disable sprites */
EINA_LIST_FOREACH(dev->sprites, l, sprite)
ecore_drm_sprites_fb_set(sprite, 0, 0);
/* close input fds ?? */
/* drop drm master */
if (ecore_drm_device_master_drop(dev))
{
/* issue ioctl to release vt */
if (!ecore_drm_tty_release(dev))
ERR("Could not release VT: %m");
}
else
ERR("Could not drop drm master: %m");
}
else if (ev->number == 2)
{
Ecore_Drm_Output *output;
Ecore_Drm_Input *input;
Eina_List *l;
DBG("Acquire VT");
/* issue ioctl to acquire vt */
if (ecore_drm_tty_acquire(dev))
{
/* set drm master */
if (!ecore_drm_device_master_set(dev))
ERR("Could not set drm master: %m");
/* set output mode */
EINA_LIST_FOREACH(dev->outputs, l, output)
ecore_drm_output_enable(output);
/* enable inputs */
EINA_LIST_FOREACH(dev->inputs, l, input)
ecore_drm_inputs_enable(input);
}
else
ERR("Could not acquire VT: %m");
}
return EINA_TRUE;
}
static Eina_Bool
_ecore_drm_tty_setup(Ecore_Drm_Device *dev)
{
struct stat st;
int kb_mode;
struct vt_mode vtmode = { 0 };
if (fstat(dev->tty.fd, &st) == -1)
{
ERR("Failed to get stats for tty: %m");
return EINA_FALSE;
}
if (ioctl(dev->tty.fd, KDGKBMODE, &kb_mode))
{
ERR("Could not get tty keyboard mode: %m");
return EINA_FALSE;
}
/* NB: Don't set this. This Turns OFF keyboard on the VT */
/* if (ioctl(dev->tty.fd, KDSKBMUTE, 1) && */
/* ioctl(dev->tty.fd, KDSKBMODE, K_OFF)) */
/* { */
/* ERR("Could not set K_OFF keyboard mode: %m"); */
/* return EINA_FALSE; */
/* } */
/* if (ioctl(dev->tty.fd, KDSETMODE, KD_GRAPHICS)) */
/* { */
/* ERR("Could not set graphics mode: %m"); */
/* return EINA_FALSE; */
/* } */
vtmode.mode = VT_PROCESS;
vtmode.waitv = 0;
vtmode.relsig = SIGUSR1;
vtmode.acqsig = SIGUSR2;
if (ioctl(dev->tty.fd, VT_SETMODE, &vtmode) < 0)
{
ERR("Could not set Terminal Mode: %m");
return EINA_FALSE;
}
/* if (ioctl(dev->tty.fd, VT_ACTIVATE, minor(st.st_rdev)) < 0) */
/* { */
/* ERR("Failed to activate vt: %m"); */
/* return EINA_FALSE; */
/* } */
/* if (ioctl(dev->tty.fd, VT_WAITACTIVE, minor(st.st_rdev)) < 0) */
/* { */
/* ERR("Failed to wait active: %m"); */
/* return EINA_FALSE; */
/* } */
return EINA_TRUE;
}
/**
* @defgroup Ecore_Drm_Tty_Group Tty manipulation functions
*
* Functions that deal with opening, closing, and otherwise using a tty
*/
/**
* Open a tty for use
*
* @param dev The Ecore_Drm_Device that this tty will belong to.
* @param name The name of the tty to try and open.
* If NULL, /dev/tty0 will be used.
*
* @return EINA_TRUE on success, EINA_FALSE on failure
*
* @ingroup Ecore_Drm_Tty_Group
*/
EAPI Eina_Bool
ecore_drm_tty_open(Ecore_Drm_Device *dev, const char *name)
{
char tty[32] = "<stdin>";
/* check for valid device */
if ((!dev) || (!dev->drm.name)) return EINA_FALSE;
/* assign default tty fd of -1 */
dev->tty.fd = -1;
if (!name)
{
char *env;
if ((env = getenv("ECORE_DRM_TTY")))
snprintf(tty, sizeof(tty), "%s", env);
else
dev->tty.fd = STDIN_FILENO;
}
else // FIXME: NB: This should Really check for format of name (/dev/xyz)
snprintf(tty, sizeof(tty), "%s", name);
if (dev->tty.fd < 0)
{
DBG("Trying to Open Tty: %s", tty);
dev->tty.fd = open(tty, O_RDWR | O_NOCTTY);
if (dev->tty.fd < 0)
{
DBG("Failed to Open Tty: %m");
return EINA_FALSE;
}
}
if (dev->tty.fd < 0)
{
DBG("Failed to open tty %s", tty);
return EINA_FALSE;
}
DBG("Opened Tty %s : %d", tty, dev->tty.fd);
/* save tty name */
dev->tty.name = eina_stringshare_add(tty);
/* FIXME */
if (!_ecore_drm_tty_setup(dev))
return EINA_FALSE;
/* setup handler for signals */
dev->tty.event_hdlr =
ecore_event_handler_add(ECORE_EVENT_SIGNAL_USER,
_ecore_drm_tty_cb_signal, dev);
/* set current tty into env */
setenv("ECORE_DRM_TTY", tty, 1);
return EINA_TRUE;
}
/**
* Close an already opened tty
*
* @param dev The Ecore_Drm_Device which owns this tty.
*
* @return EINA_TRUE on success, EINA_FALSE on failure
*
* @ingroup Ecore_Drm_Tty_Group
*/
EAPI Eina_Bool
ecore_drm_tty_close(Ecore_Drm_Device *dev)
{
/* check for valid device */
if ((!dev) || (!dev->drm.name)) return EINA_FALSE;
close(dev->tty.fd);
dev->tty.fd = -1;
/* destroy the event handler */
if (dev->tty.event_hdlr) ecore_event_handler_del(dev->tty.event_hdlr);
dev->tty.event_hdlr = NULL;
/* clear the tty name */
if (dev->tty.name) eina_stringshare_del(dev->tty.name);
dev->tty.name = NULL;
unsetenv("ECORE_DRM_TTY");
return EINA_TRUE;
}
/**
* Release a virtual terminal
*
* @param dev The Ecore_Drm_Device which owns this tty.
*
* @return EINA_TRUE on success, EINA_FALSE on failure
*
* @ingroup Ecore_Drm_Tty_Group
*/
EAPI Eina_Bool
ecore_drm_tty_release(Ecore_Drm_Device *dev)
{
/* check for valid device */
if ((!dev) || (!dev->drm.name) || (dev->tty.fd < 0)) return EINA_FALSE;
/* send ioctl for vt release */
if (ioctl(dev->tty.fd, VT_RELDISP, 1) < 0) return EINA_FALSE;
return EINA_TRUE;
}
/**
* Acquire a virtual terminal
*
* @param dev The Ecore_Drm_Device which owns this tty.
*
* @return EINA_TRUE on success, EINA_FALSE on failure
*
* @ingroup Ecore_Drm_Tty_Group
*/
EAPI Eina_Bool
ecore_drm_tty_acquire(Ecore_Drm_Device *dev)
{
/* check for valid device */
if ((!dev) || (!dev->drm.name) || (dev->tty.fd < 0)) return EINA_FALSE;
/* send ioctl for vt acquire */
if (ioctl(dev->tty.fd, VT_RELDISP, VT_ACKACQ) < 0) return EINA_FALSE;
return EINA_TRUE;
}

View File

@ -253,6 +253,12 @@ _ecore_evas_available_engines_get(void)
#endif
#ifdef BUILD_ECORE_EVAS_OPENGL_GLEW
ADDENG("opengl_glew");
#endif
}
else if (!strcmp(name, "drm"))
{
#ifdef BUILD_ECORE_EVAS_DRM
ADDENG("drm");
#endif
}
}

View File

@ -4,6 +4,7 @@
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <Eina.h>
#include <Ecore.h>
@ -16,12 +17,19 @@
#ifdef BUILD_ECORE_EVAS_DRM
# include <Evas_Engine_Drm.h>
# include <Ecore_Drm.h>
#endif
/* local structures */
typedef struct _Ecore_Evas_Engine_Data_Drm Ecore_Evas_Engine_Data_Drm;
#undef ERR
#define ERR(...) fprintf(stderr, __VA_ARGS__)
struct _Ecore_Evas_Engine_Data_Drm
#undef DBG
#define DBG(...) fprintf(stderr, __VA_ARGS__)
/* local structures */
typedef struct _Ecore_Evas_Engine_Drm_Data Ecore_Evas_Engine_Drm_Data;
struct _Ecore_Evas_Engine_Drm_Data
{
int fd;
};
@ -31,8 +39,13 @@ static int _ecore_evas_drm_init(void);
static int _ecore_evas_drm_shutdown(void);
static Ecore_Evas_Interface_Drm *_ecore_evas_drm_interface_new(void);
static void _ecore_evas_show(Ecore_Evas *ee);
static void _ecore_evas_render_updates(void *data, Evas *evas EINA_UNUSED, void *event);
static int _ecore_evas_render(Ecore_Evas *ee);
/* local variables */
static int _ecore_evas_init_count = 0;
static Ecore_Drm_Device *dev = NULL;
static Ecore_Evas_Engine_Func _ecore_evas_drm_engine_func =
{
@ -57,7 +70,7 @@ static Ecore_Evas_Engine_Func _ecore_evas_drm_engine_func =
NULL, //void (*fn_move_resize) (Ecore_Evas *ee, int x, int y, int w, int h);
NULL, //void (*fn_rotation_set) (Ecore_Evas *ee, int rot, int resize);
NULL, //void (*fn_shaped_set) (Ecore_Evas *ee, int shaped);
NULL, //void (*fn_show) (Ecore_Evas *ee);
_ecore_evas_show,
NULL, //void (*fn_hide) (Ecore_Evas *ee);
NULL, //void (*fn_raise) (Ecore_Evas *ee);
NULL, //void (*fn_lower) (Ecore_Evas *ee);
@ -84,7 +97,7 @@ static Ecore_Evas_Engine_Func _ecore_evas_drm_engine_func =
NULL, //void (*fn_transparent_set) (Ecore_Evas *ee, int transparent);
NULL, //void (*fn_profiles_set) (Ecore_Evas *ee, const char **profiles, int count);
NULL, //void (*fn_profile_set) (Ecore_Evas *ee, const char *profile);
NULL, //void (*fn_window_group_set) (Ecore_Evas *ee, const Ecore_Evas *ee_group);
NULL, //void (*fn_aspect_set) (Ecore_Evas *ee, double aspect);
NULL, //void (*fn_urgent_set) (Ecore_Evas *ee, Eina_Bool on);
@ -92,7 +105,8 @@ static Ecore_Evas_Engine_Func _ecore_evas_drm_engine_func =
NULL, //void (*fn_demands_attention_set) (Ecore_Evas *ee, Eina_Bool on);
NULL, //void (*fn_focus_skip_set) (Ecore_Evas *ee, Eina_Bool on);
NULL, //int (*fn_render) (Ecore_Evas *ee);
_ecore_evas_render,
NULL, //void (*fn_screen_geometry_get) (const Ecore_Evas *ee, int *x, int *y, int *w, int *h);
NULL, //void (*fn_screen_dpi_get) (const Ecore_Evas *ee, int *xdpi, int *ydpi);
NULL, //void (*fn_msg_parent_send) (Ecore_Evas *ee, int maj, int min, void *data, int size);
@ -103,6 +117,8 @@ EAPI Ecore_Evas *
ecore_evas_drm_new_internal(const char *device, unsigned int parent, int x, int y, int w, int h)
{
Ecore_Evas *ee;
Evas_Engine_Info_Drm *einfo;
Ecore_Evas_Interface_Drm *iface;
int method;
/* try to find the evas drm engine */
@ -112,36 +128,303 @@ ecore_evas_drm_new_internal(const char *device, unsigned int parent, int x, int
return NULL;
}
/* try to init drm and company */
if (_ecore_evas_drm_init() < 1) return NULL;
/* try to allocate space for Ecore_Evas structure */
if (!(ee = calloc(1, sizeof(Ecore_Evas))))
{
ERR("Failed to allocate space for new Ecore_Evas");
return NULL;
goto ee_err;
}
/* FIXME: STORE Ecore_Drm_Dev in engine somewhere ?? */
ECORE_MAGIC_SET(ee, ECORE_MAGIC_EVAS);
ee->engine.func = (Ecore_Evas_Engine_Func *)&_ecore_evas_drm_engine_func;
iface = _ecore_evas_drm_interface_new();
ee->engine.ifaces = eina_list_append(ee->engine.ifaces, iface);
/* set some engine properties */
ee->driver = "drm";
if (device) ee->name = strdup(device);
if (w < 1) w = 1;
if (h < 1) h = 1;
ee->x = ee->req.x = x;
ee->y = ee->req.y = y;
ee->w = ee->req.w = w;
ee->h = ee->req.h = h;
ee->prop.max.w = 32767;
ee->prop.max.h = 32767;
ee->prop.layer = 4;
ee->prop.request_pos = 0;
ee->prop.sticky = 0;
ee->alpha = EINA_FALSE;
ee->can_async_render = 1;
if (getenv("ECORE_EVAS_FORCE_SYNC_RENDER"))
ee->can_async_render = 0;
/* try to initialize evas */
ee->evas = evas_new();
evas_data_attach_set(ee->evas, ee);
evas_output_method_set(ee->evas, method);
/* FIXME: Support initial rotation ?? */
evas_output_size_set(ee->evas, w, h);
evas_output_viewport_set(ee->evas, 0, 0, w, h);
if (ee->can_async_render)
evas_event_callback_add(ee->evas, EVAS_CALLBACK_RENDER_POST,
_ecore_evas_render_updates, ee);
if ((einfo = (Evas_Engine_Info_Drm *)evas_engine_info_get(ee->evas)))
{
einfo->info.depth = 32; // FIXME
einfo->info.destination_alpha = ee->alpha;
einfo->info.rotation = ee->rotation;
einfo->info.fd = ecore_drm_device_fd_get(dev);
if (!evas_engine_info_set(ee->evas, (Evas_Engine_Info *)einfo))
{
ERR("evas_engine_info_set() for engine '%s' failed.", ee->driver);
goto eng_err;
}
}
else
{
ERR("Failed to get Evas Engine Info for '%s'", ee->driver);
goto eng_err;
}
_ecore_evas_register(ee);
ecore_evas_input_event_register(ee);
return ee;
eng_err:
ecore_evas_free(ee);
ee_err:
_ecore_evas_drm_shutdown();
return NULL;
}
/* local functions */
static int
_ecore_evas_drm_init(void)
{
_ecore_evas_init_count++;
if (_ecore_evas_init_count > 1) return _ecore_evas_init_count;
if (++_ecore_evas_init_count != 1)
return _ecore_evas_init_count;
/* try to init ecore_drm */
if (!ecore_drm_init())
{
ERR("Could not initialize Ecore_Drm");
return --_ecore_evas_init_count;
}
/* try to find the device */
if (!(dev = ecore_drm_device_find(NULL, NULL)))
{
ERR("Could not find default drm device");
goto dev_err;
}
/* try to open the graphics card */
if (!ecore_drm_device_open(dev))
{
ERR("Could not open drm device");
goto dev_open_err;
}
/* try to open the tty */
if (!ecore_drm_tty_open(dev, NULL))
{
ERR("Could not open tty: %m");
goto tty_open_err;
}
/* FIXME: Init egl/software renderer here ?? */
/* try to create sprites */
if (!ecore_drm_sprites_create(dev))
{
ERR("Could not create sprites: %m");
goto sprite_err;
}
/* try to create outputs */
if (!ecore_drm_outputs_create(dev))
{
ERR("Could not create outputs: %m");
goto output_err;
}
/* try to create inputs */
if (!ecore_drm_inputs_create(dev))
{
ERR("Could not create inputs: %m");
goto output_err;
}
ecore_event_evas_init();
return _ecore_evas_init_count;
output_err:
ecore_drm_sprites_destroy(dev);
sprite_err:
ecore_drm_tty_close(dev);
tty_open_err:
ecore_drm_device_close(dev);
dev_open_err:
ecore_drm_device_free(dev);
dev_err:
ecore_drm_shutdown();
return --_ecore_evas_init_count;
}
static int
_ecore_evas_drm_shutdown(void)
{
_ecore_evas_init_count--;
if (_ecore_evas_init_count == 0)
{
ecore_event_evas_shutdown();
}
if (--_ecore_evas_init_count != 0)
return _ecore_evas_init_count;
ecore_drm_sprites_destroy(dev);
/* NB: No need to free outputs here. Is done in device free */
ecore_drm_inputs_destroy(dev);
ecore_drm_tty_close(dev);
ecore_drm_device_close(dev);
ecore_drm_device_free(dev);
ecore_drm_shutdown();
ecore_event_evas_shutdown();
if (_ecore_evas_init_count < 0) _ecore_evas_init_count = 0;
return _ecore_evas_init_count;
}
static Ecore_Evas_Interface_Drm *
_ecore_evas_drm_interface_new(void)
{
Ecore_Evas_Interface_Drm *iface;
if (!(iface = calloc(1, sizeof(Ecore_Evas_Interface_Drm))))
return NULL;
iface->base.name = "drm";
iface->base.version = 1;
/* iface->pixmap_visual_get; */
/* iface->pixmap_colormap_get; */
/* iface->pixmap_depth_get; */
return iface;
}
static void
_ecore_evas_show(Ecore_Evas *ee)
{
if ((!ee) || (ee->visible)) return;
DBG("Show Ecore_Evas\n");
/* TODO: fixme */
evas_damage_rectangle_add(ee->evas, 0, 0, ee->w, ee->h);
ee->visible = 1;
if (ee->func.fn_show) ee->func.fn_show(ee);
}
static int
_ecore_evas_render_updates_process(Ecore_Evas *ee, Eina_List *updates)
{
int rend = 0;
DBG("\tRender Updates Process\n");
if ((ee->visible) && (updates))
{
Eina_List *l;
Eina_Rectangle *r;
EINA_LIST_FOREACH(updates, l, r)
{
/* TODO: damage window */
DBG("\t\tDamage Window: %d %d %d %d\n", r->x, r->y, r->w, r->h);
}
_ecore_evas_idle_timeout_update(ee);
rend = 1;
}
else
evas_norender(ee->evas);
if (ee->func.fn_post_render) ee->func.fn_post_render(ee);
return rend;
}
static void
_ecore_evas_render_updates(void *data, Evas *evas EINA_UNUSED, void *event)
{
Ecore_Evas *ee;
Evas_Event_Render_Post *ev;
if (!(ev = event)) return;
if (!(ee = data)) return;
ee->in_async_render = EINA_FALSE;
_ecore_evas_render_updates_process(ee, ev->updated_area);
/* TODO: handle delayed setting */
}
static int
_ecore_evas_render(Ecore_Evas *ee)
{
int rend = 0;
Eina_List *l;
Ecore_Evas *ee2;
if (!ee) return 0;
if (ee->in_async_render) return 0;
if (!ee->visible)
{
evas_norender(ee->evas);
return 0;
}
/* DBG("Render Ecore_Evas\n"); */
EINA_LIST_FOREACH(ee->sub_ecore_evas, l, ee2)
{
if (ee2->func.fn_pre_render) ee2->func.fn_pre_render(ee2);
if (ee2->engine.func->fn_render)
rend |= ee2->engine.func->fn_render(ee2);
if (ee2->func.fn_post_render) ee2->func.fn_post_render(ee2);
}
if (ee->func.fn_pre_render) ee->func.fn_pre_render(ee);
if (!ee->can_async_render)
{
Eina_List *updates;
if ((updates = evas_render_updates(ee->evas)))
{
rend = _ecore_evas_render_updates_process(ee, updates);
evas_render_updates_free(updates);
}
}
else if (evas_render_async(ee->evas))
{
ee->in_async_render = EINA_TRUE;
rend = 1;
}
return rend;
}

View File

@ -11,6 +11,10 @@ struct _Evas_Engine_Info_Drm
struct
{
void *gbm;
unsigned int format;
int fd;
unsigned int rotation, depth;
Eina_Bool destination_alpha : 1;
} info;

View File

@ -0,0 +1,88 @@
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include "evas_engine.h"
#include <dlfcn.h>
static int _init_count = 0;
/* the actual Manager device returned from init of the library */
static void *bufmgr;
/* the actual library we successfully dlopen'd */
static void *libbufmgr;
/* pointers to library functions that we need */
void *(*libbufmgr_init)(int fd);
void (*libbufmgr_shutdown)(void *mgr);
//void *(*libbufmgr_buffer_create)(void *mgr);
int
evas_buffer_manager_init(void)
{
LOGFN(__FILE__, __LINE__, __FUNCTION__);
if (++_init_count != 1) return _init_count;
/* try to dlopen various buffer managers until we find one that works
* (libgbm, libtbm, etc)
*
* NB: as I am testing this on desktop and do not have libtbm, I am
* just going to code for that. Feel free to hack in libtbm support */
libbufmgr = dlopen("libgbm.so", (RTLD_LAZY | RTLD_GLOBAL));
if (!libbufmgr)
libbufmgr = dlopen("libgbm.so.1", (RTLD_LAZY | RTLD_GLOBAL));
if (!libbufmgr)
libbufmgr = dlopen("libgbm.so.1.0.0", (RTLD_LAZY | RTLD_GLOBAL));
if (!libbufmgr) return --_init_count;
/* with the library found, symlink the functions we need */
libbufmgr_init = dlsym(libbufmgr, "gbm_create_device");
libbufmgr_shutdown = dlsym(libbufmgr, "gbm_device_destroy");
return _init_count;
}
int
evas_buffer_manager_shutdown(void)
{
LOGFN(__FILE__, __LINE__, __FUNCTION__);
if (--_init_count != 0) return _init_count;
/* call function to shutdown the manager */
evas_buffer_manager_close();
}
Eina_Bool
evas_buffer_manager_open(int fd)
{
Eina_Bool ret = EINA_FALSE;
LOGFN(__FILE__, __LINE__, __FUNCTION__);
if (bufmgr) return EINA_TRUE;
/* call any init functions required by the dlsym'd buffer manager */
if (libbufmgr_init)
{
if ((bufmgr = libbufmgr_init(fd)))
ret = EINA_TRUE;
}
return ret;
}
void
evas_buffer_manager_close(void)
{
LOGFN(__FILE__, __LINE__, __FUNCTION__);
/* call any shutdown functions required by the dlsym'd buffer manager */
if (libbufmgr_shutdown)
libbufmgr_shutdown(bufmgr);
bufmgr = NULL;
}

View File

@ -1,6 +1,3 @@
#include "evas_common_private.h"
#include "evas_private.h"
#include "Evas_Engine_Drm.h"
#include "evas_engine.h"
/* local structures */
@ -9,112 +6,207 @@ typedef struct _Render_Engine Render_Engine;
struct _Render_Engine
{
Evas_Engine_Info_Drm *info;
Outbuf *ob;
Tilebuf *tb;
Tilebuf_Rect *rects;
Tilebuf_Rect *prev_rects[3];
Outbuf *ob;
Eina_Inlist *cur_rect;
short mode;
Eina_Inlist *cur_rect;
Eina_Bool end : 1;
Eina_Bool lost_back : 1;
/* function pointers for output buffer functions that we can
* override based on if we are swapping or not */
void (*outbuf_free)(Outbuf *ob);
void (*outbuf_reconfigure)(Outbuf *ob, int x, int y, int w, int h, unsigned int rotation, Outbuf_Depth depth, Eina_Bool alpha);
RGBA_Image *(*outbuf_update_region_new)(Outbuf *ob, int x, int y, int w, int h, int *cx, int *cy, int *cw, int *ch);
void (*outbuf_update_region_push)(Outbuf *ob, RGBA_Image *update, int x, int y, int w, int h);
void (*outbuf_update_region_free)(Outbuf *ob, RGBA_Image *update);
void (*outbuf_flush)(Outbuf *ob);
void (*outbuf_idle_flush)(Outbuf *ob);
};
/* local function prototypes */
static void *_output_setup(int w, int h, unsigned int rotation, unsigned int depth, Eina_Bool alpha, int swap);
static void *_output_engine_setup(int w, int h, unsigned int rotation, unsigned int depth, Eina_Bool destination_alpha, int fd, int try_swap);
static Tilebuf_Rect *_merge_rects(Tilebuf *tb, Tilebuf_Rect *r1, Tilebuf_Rect *r2, Tilebuf_Rect *r3);
/* function tables - filled in later (func and parent func) */
/* engine function prototypes */
static void *eng_info(Evas *eo_evas EINA_UNUSED);
static void eng_info_free(Evas *eo_evas EINA_UNUSED, void *einfo);
static int eng_setup(Evas *eo_evas, void *einfo);
static void eng_output_free(void *data);
static void eng_output_resize(void *data, int w, int h);
static void eng_output_tile_size_set(void *data, int w, int h);
static void eng_output_redraws_rect_add(void *data, int x, int y, int w, int h);
static void eng_output_redraws_rect_del(void *data, int x, int y, int w, int h);
static void eng_output_redraws_clear(void *data);
static void *eng_output_redraws_next_update_get(void *data, int *x, int *y, int *w, int *h, int *cx, int *cy, int *cw, int *ch);
static void eng_output_redraws_next_update_push(void *data, void *surface, int x, int y, int w, int h, Evas_Render_Mode render_mode);
static void eng_output_flush(void *data, Evas_Render_Mode render_mode);
static void eng_output_idle_flush(void *data);
/* local variables */
static Evas_Func func, pfunc;
/* external variables */
int _evas_engine_drm_log_dom;
int _evas_engine_drm_log_dom = -1;
/* local functions */
static void *
_output_setup(int w, int h, unsigned int rotation, unsigned int depth, Eina_Bool alpha, int swap)
_output_engine_setup(int w, int h, unsigned int rotation, unsigned int depth, Eina_Bool destination_alpha, int fd, int try_swap)
{
Render_Engine *re;
Render_Engine *re = NULL;
/* try to allocate space for our render engine structure */
if (!(re = calloc(1, sizeof(Render_Engine))))
LOGFN(__FILE__, __LINE__, __FUNCTION__);
/* try to allocate a new render engine */
if (!(re = calloc(1, sizeof(Render_Engine))))
return NULL;
/* try to create a new tilebuffer */
/* try to create a new tilebuf first */
if (!(re->tb = evas_common_tilebuf_new(w, h)))
{
free(re);
return NULL;
}
/* set tilesize */
/* set tile size for the tile buffer */
evas_common_tilebuf_set_tile_size(re->tb, TILESIZE, TILESIZE);
if (swap)
if (try_swap)
{
/* free any existing outbuf */
if (re->ob) evas_outbuf_free(re->ob);
/* try to create new outbuf */
if (!(re->ob = evas_outbuf_setup(w, h, rotation, depth, alpha)))
if ((re->ob = evas_swapbuf_setup(w, h, rotation, depth,
destination_alpha, fd)))
{
if (re->tb) evas_common_tilebuf_free(re->tb);
free(re);
return NULL;
re->outbuf_free = evas_swapbuf_free;
re->outbuf_reconfigure = evas_swapbuf_reconfigure;
re->outbuf_update_region_new = evas_swapbuf_update_region_new;
re->outbuf_update_region_push = evas_swapbuf_update_region_push;
re->outbuf_update_region_free = evas_swapbuf_update_region_free;
re->outbuf_flush = evas_swapbuf_flush;
re->outbuf_idle_flush = evas_swapbuf_idle_flush;
}
}
/* return the allocated render_engine structure */
/* if creating an output buffer failed, then return NULL */
if (!re->ob)
{
if (re->tb) evas_common_tilebuf_free(re->tb);
free(re);
return NULL;
}
/* return allocated render engine */
return re;
}
/* engine api functions */
static Tilebuf_Rect *
_merge_rects(Tilebuf *tb, Tilebuf_Rect *r1, Tilebuf_Rect *r2, Tilebuf_Rect *r3)
{
Tilebuf_Rect *r, *rects;
LOGFN(__FILE__, __LINE__, __FUNCTION__);
if (r1)
{
EINA_INLIST_FOREACH(EINA_INLIST_GET(r1), r)
evas_common_tilebuf_add_redraw(tb, r->x, r->y, r->w, r->h);
}
if (r2)
{
EINA_INLIST_FOREACH(EINA_INLIST_GET(r2), r)
evas_common_tilebuf_add_redraw(tb, r->x, r->y, r->w, r->h);
}
if (r3)
{
EINA_INLIST_FOREACH(EINA_INLIST_GET(r3), r)
evas_common_tilebuf_add_redraw(tb, r->x, r->y, r->w, r->h);
}
rects = evas_common_tilebuf_get_render_rects(tb);
/*
// bounding box -> make a bounding box single region update of all regions.
// yes we could try and be smart and figure out size of regions, how far
// apart etc. etc. to try and figure out an optimal "set". this is a tradeoff
// between multiple update regions to render and total pixels to render.
if (rects)
{
px1 = rects->x; py1 = rects->y;
px2 = rects->x + rects->w; py2 = rects->y + rects->h;
EINA_INLIST_FOREACH(EINA_INLIST_GET(rects), r)
{
if (r->x < x1) px1 = r->x;
if (r->y < y1) py1 = r->y;
if ((r->x + r->w) > x2) px2 = r->x + r->w;
if ((r->y + r->h) > y2) py2 = r->y + r->h;
}
evas_common_tilebuf_free_render_rects(rects);
rects = calloc(1, sizeof(Tilebuf_Rect));
if (rects)
{
rects->x = px1;
rects->y = py1;
rects->w = px2 - px1;
rects->h = py2 - py1;
}
}
*/
evas_common_tilebuf_clear(tb);
return rects;
}
/* engine functions */
static void *
eng_info(Evas *evas EINA_UNUSED)
eng_info(Evas *eo_evas EINA_UNUSED)
{
Evas_Engine_Info_Drm *info;
/* try to allocate space for our engine info structure */
/* try to allocate space for engine info */
if (!(info = calloc(1, sizeof(Evas_Engine_Info_Drm))))
return NULL;
/* set some engine default properties */
/* fill in default engine info fields */
info->magic.magic = rand();
info->render_mode = EVAS_RENDER_MODE_BLOCKING;
/* return allocated engine info */
return info;
}
static void
eng_info_free(Evas *evas EINA_UNUSED, void *einfo)
eng_info_free(Evas *eo_evas EINA_UNUSED, void *einfo)
{
Evas_Engine_Info_Drm *info;
/* free the engine info */
/* try to free previously allocated engine info */
if ((info = (Evas_Engine_Info_Drm *)einfo))
free(info);
}
static int
eng_setup(Evas *evas, void *einfo)
eng_setup(Evas *eo_evas, void *einfo)
{
Evas_Engine_Info_Drm *info;
Evas_Public_Data *epd;
Render_Engine *re;
Render_Engine *re = NULL;
/* try to cast to our engine info structure */
if (!(info = (Evas_Engine_Info_Drm *)einfo)) return 0;
LOGFN(__FILE__, __LINE__, __FUNCTION__);
/* try to get the evas public data */
if (!(epd = eo_data_scope_get(evas, EVAS_CLASS))) return 0;
/* try to cast the engine info to our engine info */
if (!(info = (Evas_Engine_Info_Drm *)einfo))
return 0;
/* check for valid engine output */
/* try to get evas public data from the canvas */
if (!(epd = eo_data_scope_get(eo_evas, EVAS_CLASS)))
return 0;
/* test for valid engine output */
if (!(re = epd->engine.data.output))
{
static int swap = -1;
static int try_swap = -1;
/* NB: If we have no valid output then assume we have not been
* initialized yet and call any needed common init routines */
@ -130,47 +222,74 @@ eng_setup(Evas *evas, void *einfo)
evas_common_draw_init();
evas_common_tilebuf_init();
/* check if swapping is disabled */
if (swap == -1)
if (try_swap == -1)
{
if (getenv("EVAS_DRM_NO_SWAP")) swap = 0;
else swap = 1;
/* check for env var to see if we should try swapping */
if (getenv("EVAS_NO_DRM_SWAPBUF")) try_swap = 0;
else try_swap = 1;
}
/* try to create a new render_engine */
if (!(re = _output_setup(epd->output.w, epd->output.h,
info->info.rotation, info->info.depth,
info->info.destination_alpha, swap)))
/* ensure the buffer manager has been opened */
if (!evas_buffer_manager_open(info->info.fd))
{
fprintf(stderr, "COULD NOT OPEN BUFFER MANAGER !!!\n");
return 0;
}
if (!(re =
_output_engine_setup(epd->output.w, epd->output.h,
info->info.rotation, info->info.depth,
info->info.destination_alpha,
info->info.fd, try_swap)))
return 0;
re->info = info;
}
else
{
/* if we have an existing outbuf, free it */
if (re->ob) evas_outbuf_free(re->ob);
int ponebuf = 0;
/* try to create a new outbuf */
if (!(re->ob =
evas_outbuf_setup(epd->output.w, epd->output.h,
info->info.rotation, info->info.depth,
info->info.destination_alpha)))
return 0;
if ((re) && (re->ob)) ponebuf = re->ob->onebuf;
/* free any existing tile buffer */
if (re->tb) evas_common_tilebuf_free(re->tb);
/* we have an existing output buffer, free it */
if (re->ob) re->outbuf_free(re->ob);
/* create new tile buffer */
if ((re->tb = evas_common_tilebuf_new(epd->output.w, epd->output.h)))
evas_common_tilebuf_set_tile_size(re->tb, TILESIZE, TILESIZE);
if ((re->ob = evas_swapbuf_setup(epd->output.w, epd->output.h,
info->info.rotation,
info->info.depth,
info->info.destination_alpha,
info->info.fd)))
{
re->outbuf_free = evas_swapbuf_free;
re->outbuf_reconfigure = evas_swapbuf_reconfigure;
re->outbuf_update_region_new = evas_swapbuf_update_region_new;
re->outbuf_update_region_push = evas_swapbuf_update_region_push;
re->outbuf_update_region_free = evas_swapbuf_update_region_free;
re->outbuf_flush = evas_swapbuf_flush;
re->outbuf_idle_flush = evas_swapbuf_idle_flush;
}
re->info = info;
if ((re) && (re->ob)) re->ob->onebuf = ponebuf;
}
/* update the info structure pointer */
re->info = info;
/* reassign engine output */
/* reassign render engine to output */
epd->engine.data.output = re;
if (!epd->engine.data.output) return 0;
/* check for valid engine context */
if (!epd->engine.data.context)
{
/* create a context if needed */
epd->engine.data.context =
epd->engine.func->context_new(epd->engine.data.output);
}
/* return success */
return 1;
}
@ -179,16 +298,19 @@ eng_output_free(void *data)
{
Render_Engine *re;
LOGFN(__FILE__, __LINE__, __FUNCTION__);
if ((re = data))
{
if (re->ob) evas_outbuf_free(re->ob);
if (re->tb) evas_common_tilebuf_free(re->tb);
if (re->rects) evas_common_tilebuf_free_render_rects(re->rects);
if (re->prev_rects[0])
re->outbuf_free(re->ob);
evas_common_tilebuf_free(re->tb);
if (re->rects)
evas_common_tilebuf_free_render_rects(re->rects);
if (re->prev_rects[0])
evas_common_tilebuf_free_render_rects(re->prev_rects[0]);
if (re->prev_rects[1])
if (re->prev_rects[1])
evas_common_tilebuf_free_render_rects(re->prev_rects[1]);
if (re->prev_rects[2])
if (re->prev_rects[2])
evas_common_tilebuf_free_render_rects(re->prev_rects[2]);
free(re);
}
@ -197,52 +319,311 @@ eng_output_free(void *data)
evas_common_image_shutdown();
}
/* module api functions */
static int
static void
eng_output_resize(void *data, int w, int h)
{
Render_Engine *re;
Evas_Engine_Info_Drm *info;
/* int dx = 0, dy = 0; */
LOGFN(__FILE__, __LINE__, __FUNCTION__);
if (!(re = (Render_Engine *)data)) return;
if (!(info = re->info)) return;
/* if (info->info.edges & 4) */
/* { */
/* if ((info->info.rotation == 90) || (info->info.rotation == 270)) */
/* dx = re->ob->h - h; */
/* else */
/* dx = re->ob->w - w; */
/* } */
/* if (info->info.edges & 1) */
/* { */
/* if ((info->info.rotation == 90) || (info->info.rotation == 270)) */
/* dy = re->ob->w - w; */
/* else */
/* dy = re->ob->h - h; */
/* } */
re->outbuf_reconfigure(re->ob, 0, 0, w, h,
info->info.rotation, info->info.depth,
info->info.destination_alpha);
evas_common_tilebuf_free(re->tb);
if ((re->tb = evas_common_tilebuf_new(w, h)))
evas_common_tilebuf_set_tile_size(re->tb, TILESIZE, TILESIZE);
}
static void
eng_output_tile_size_set(void *data, int w, int h)
{
Render_Engine *re;
if (!(re = (Render_Engine *)data)) return;
if (re->tb) evas_common_tilebuf_set_tile_size(re->tb, w, h);
}
static void
eng_output_redraws_rect_add(void *data, int x, int y, int w, int h)
{
Render_Engine *re;
if (!(re = (Render_Engine *)data)) return;
evas_common_tilebuf_add_redraw(re->tb, x, y, w, h);
}
static void
eng_output_redraws_rect_del(void *data, int x, int y, int w, int h)
{
Render_Engine *re;
if (!(re = (Render_Engine *)data)) return;
if (re->tb) evas_common_tilebuf_del_redraw(re->tb, x, y, w, h);
}
static void
eng_output_redraws_clear(void *data)
{
Render_Engine *re;
if (!(re = (Render_Engine *)data)) return;
if (re->tb) evas_common_tilebuf_clear(re->tb);
}
static void *
eng_output_redraws_next_update_get(void *data, int *x, int *y, int *w, int *h, int *cx, int *cy, int *cw, int *ch)
{
Render_Engine *re;
RGBA_Image *surface;
Tilebuf_Rect *rect;
Eina_Bool first_rect = EINA_FALSE;
LOGFN(__FILE__, __LINE__, __FUNCTION__);
#define CLEAR_PREV_RECTS(x) \
do { \
if (re->prev_rects[x]) \
evas_common_tilebuf_free_render_rects(re->prev_rects[x]); \
re->prev_rects[x] = NULL; \
} while (0)
re = (Render_Engine *)data;
if (re->end)
{
re->end = 0;
return NULL;
}
if (!re->rects)
{
re->mode = evas_swapbuf_state_get(re->ob);
re->rects = evas_common_tilebuf_get_render_rects(re->tb);
if (re->rects)
{
if ((re->lost_back) || (re->mode == MODE_FULL))
{
/* if we lost our backbuffer since the last frame redraw all */
re->lost_back = 0;
evas_common_tilebuf_add_redraw(re->tb, 0, 0, re->ob->w, re->ob->h);
evas_common_tilebuf_free_render_rects(re->rects);
re->rects = evas_common_tilebuf_get_render_rects(re->tb);
}
/* ensure we get rid of previous rect lists we dont need if mode
* changed/is appropriate */
evas_common_tilebuf_clear(re->tb);
CLEAR_PREV_RECTS(2);
re->prev_rects[2] = re->prev_rects[1];
re->prev_rects[1] = re->prev_rects[0];
re->prev_rects[0] = re->rects;
re->rects = NULL;
switch (re->mode)
{
case MODE_FULL:
case MODE_COPY: // no prev rects needed
re->rects =
_merge_rects(re->tb, re->prev_rects[0], NULL, NULL);
break;
case MODE_DOUBLE: // double mode - only 1 level of prev rect
re->rects =
_merge_rects(re->tb, re->prev_rects[0], re->prev_rects[1], NULL);
break;
case MODE_TRIPLE: // keep all
re->rects =
_merge_rects(re->tb, re->prev_rects[0], re->prev_rects[1], re->prev_rects[2]);
break;
default:
break;
}
first_rect = EINA_TRUE;
}
evas_common_tilebuf_clear(re->tb);
re->cur_rect = EINA_INLIST_GET(re->rects);
}
if (!re->cur_rect) return NULL;
rect = (Tilebuf_Rect *)re->cur_rect;
if (re->rects)
{
switch (re->mode)
{
case MODE_COPY:
case MODE_DOUBLE:
case MODE_TRIPLE:
rect = (Tilebuf_Rect *)re->cur_rect;
*x = rect->x;
*y = rect->y;
*w = rect->w;
*h = rect->h;
*cx = rect->x;
*cy = rect->y;
*cw = rect->w;
*ch = rect->h;
re->cur_rect = re->cur_rect->next;
break;
case MODE_FULL:
re->cur_rect = NULL;
if (x) *x = 0;
if (y) *y = 0;
if (w) *w = re->ob->w;
if (h) *h = re->ob->h;
if (cx) *cx = 0;
if (cy) *cy = 0;
if (cw) *cw = re->ob->w;
if (ch) *ch = re->ob->h;
break;
default:
break;
}
if (first_rect)
{
// do anything needed for the first frame
}
surface =
re->outbuf_update_region_new(re->ob,
*x, *y, *w, *h,
cx, cy, cw, ch);
if (!re->cur_rect) re->end = EINA_TRUE;
return surface;
}
return NULL;
}
static void
eng_output_redraws_next_update_push(void *data, void *surface, int x, int y, int w, int h, Evas_Render_Mode render_mode)
{
Render_Engine *re;
LOGFN(__FILE__, __LINE__, __FUNCTION__);
if (render_mode == EVAS_RENDER_MODE_ASYNC_INIT) return;
if (!(re = (Render_Engine *)data)) return;
#if defined(BUILD_PIPE_RENDER)
evas_common_pipe_map_begin(surface);
#endif
re->outbuf_update_region_push(re->ob, surface, x, y, w, h);
re->outbuf_update_region_free(re->ob, surface);
evas_common_cpu_end_opt();
}
static void
eng_output_flush(void *data, Evas_Render_Mode render_mode)
{
Render_Engine *re;
LOGFN(__FILE__, __LINE__, __FUNCTION__);
if (render_mode == EVAS_RENDER_MODE_ASYNC_INIT) return;
if (!(re = (Render_Engine *)data)) return;
re->outbuf_flush(re->ob);
if (re->rects)
{
evas_common_tilebuf_free_render_rects(re->rects);
re->rects = NULL;
}
}
static void
eng_output_idle_flush(void *data)
{
Render_Engine *re;
LOGFN(__FILE__, __LINE__, __FUNCTION__);
if (!(re = (Render_Engine *)data)) return;
re->outbuf_idle_flush(re->ob);
}
/* module functions */
static int
module_open(Evas_Module *em)
{
/* check for valid evas module */
if (!em) return 0;
/* try to inherit functions from software_generic engine */
if (!_evas_module_engine_inherit(&pfunc, "software_generic")) return 0;
/* try to create eina logging domain */
/* try to create our logging domain */
_evas_engine_drm_log_dom =
eina_log_domain_register("evas-drm", EVAS_DEFAULT_LOG_COLOR);
/* if we could not create a logging domain, error out */
if (_evas_engine_drm_log_dom < 0)
{
EINA_LOG_ERR("Can not create a module log domain.");
/* creating the logging domain failed. notify user */
EINA_LOG_ERR("Could not create a module log domain.");
/* return failure */
return 0;
}
/* store parent functions */
/* try to inherit base functions from the software generic engine */
if (!_evas_module_engine_inherit(&pfunc, "software_generic"))
return 0;
/* try to init the buffer manager */
if (!evas_buffer_manager_init()) return 0;
/* copy base functions from the software_generic engine */
func = pfunc;
/* override the methods we provide */
EVAS_API_OVERRIDE(info, &func, eng_);
EVAS_API_OVERRIDE(info_free, &func, eng_);
EVAS_API_OVERRIDE(setup, &func, eng_);
EVAS_API_OVERRIDE(output_free, &func, eng_);
/* override any engine specific functions that we provide */
#define ORD(f) EVAS_API_OVERRIDE(f, &func, eng_)
ORD(info);
ORD(info_free);
ORD(setup);
ORD(output_free);
ORD(output_resize);
ORD(output_tile_size_set);
ORD(output_redraws_rect_add);
ORD(output_redraws_rect_del);
ORD(output_redraws_clear);
ORD(output_redraws_next_update_get);
ORD(output_redraws_next_update_push);
ORD(output_flush);
ORD(output_idle_flush);
/* advertise our engine functions */
/* advertise out our own api */
em->functions = (void *)(&func);
/* return success */
return 1;
}
static void
module_close(Evas_Module *em EINA_UNUSED)
{
/* unregister the eina log domain for this engine */
eina_log_domain_unregister(_evas_engine_drm_log_dom);
/* shutdown the buffer manager */
evas_buffer_manager_shutdown();
/* if we have the log domain, unregister it */
if (_evas_engine_drm_log_dom > -1)
eina_log_domain_unregister(_evas_engine_drm_log_dom);
}
static Evas_Module_Api evas_modapi =
static Evas_Module_Api evas_modapi =
{
EVAS_MODULE_API_VERSION, "drm", "none", { module_open, module_close }
EVAS_MODULE_API_VERSION, "drm", "none", {module_open, module_close}
};
EVAS_MODULE_DEFINE(EVAS_MODULE_TYPE_ENGINE, engine, drm);

View File

@ -1,5 +1,29 @@
#ifndef EVAS_ENGINE_H
# define EVAS_ENGINE_H
#ifndef _EVAS_ENGINE_H
# define _EVAS_ENGINE_H
# define LOGFNS 1
# ifdef LOGFNS
# include <stdio.h>
# define LOGFN(fl, ln, fn) printf("-EVAS-DRM: %25s: %5i - %s\n", fl, ln, fn);
# else
# define LOGFN(fl, ln, fn)
# endif
# include "evas_common_private.h"
# include "evas_macros.h"
# include "evas_private.h"
# include "Evas.h"
# include "Evas_Engine_Drm.h"
/* # include <xf86drm.h> */
/* # include <xf86drmMode.h> */
/* # include <drm_fourcc.h> */
/* # ifdef GL_GLES */
/* # include <EGL/egl.h> */
/* # define GL_GLEXT_PROTOTYPES */
/* # endif */
extern int _evas_engine_drm_log_dom;
@ -28,9 +52,19 @@ extern int _evas_engine_drm_log_dom;
# endif
# define CRI(...) EINA_LOG_DOM_CRIT(_evas_engine_drm_log_dom, __VA_ARGS__)
typedef enum _Outbuf_Depth Outbuf_Depth;
typedef struct _Wl_Swapper Wl_Swapper;
typedef struct _Outbuf Outbuf;
enum
enum _Outbuf_Depth
{
OUTBUF_DEPTH_NONE,
OUTBUF_DEPTH_ARGB_32BPP_8888_8888,
OUTBUF_DEPTH_RGB_32BPP_8888_8888,
OUTBUF_DEPTH_LAST
};
enum
{
MODE_FULL,
MODE_COPY,
@ -41,12 +75,54 @@ enum
struct _Outbuf
{
int w, h;
unsigned int rotation, depth;
Eina_Bool destination_alpha : 1;
unsigned int rotation;
Outbuf_Depth depth;
int onebuf;
struct
{
/* file descriptor for drm card */
int fd;
/* swapper */
void *swapper;
/* one big buffer for updates. flushed on idle_flush */
RGBA_Image *onebuf;
Eina_Array onebuf_regions;
/* a list of pending regions to write out */
Eina_List *pending_writes;
/* list of previous frame pending regions to write out */
Eina_List *prev_pending_writes;
Eina_Bool destination_alpha : 1;
} priv;
};
Outbuf *evas_outbuf_setup(int w, int h, unsigned int rotation, unsigned int depth, Eina_Bool alpha);
void evas_outbuf_free(Outbuf *ob);
void evas_outbuf_reconfigure(Outbuf *ob, int w, int h, unsigned int rotation, unsigned int depth, Eina_Bool alpha);
Outbuf *evas_swapbuf_setup(int w, int h, unsigned int rotation, Outbuf_Depth depth, Eina_Bool alpha, int fd);
void evas_swapbuf_free(Outbuf *ob);
void evas_swapbuf_reconfigure(Outbuf *ob, int x, int y, int w, int h, unsigned int rotation, Outbuf_Depth depth, Eina_Bool alpha);
RGBA_Image *evas_swapbuf_update_region_new(Outbuf *ob, int x, int y, int w, int h, int *cx, int *cy, int *cw, int *ch);
void evas_swapbuf_update_region_push(Outbuf *ob, RGBA_Image *update, int x, int y, int w, int h);
void evas_swapbuf_update_region_free(Outbuf *ob, RGBA_Image *update);
void evas_swapbuf_flush(Outbuf *ob);
void evas_swapbuf_idle_flush(Outbuf *ob EINA_UNUSED);
int evas_swapbuf_state_get(Outbuf *ob);
Wl_Swapper *evas_swapper_setup(int dx, int dy, int w, int h, Outbuf_Depth depth, Eina_Bool alpha, int fd);
Wl_Swapper *evas_swapper_reconfigure(Wl_Swapper *ws, int dx, int dy, int w, int h, Outbuf_Depth depth, Eina_Bool alpha);
void evas_swapper_swap(Wl_Swapper *ws, Eina_Rectangle *rects, unsigned int count);
void evas_swapper_free(Wl_Swapper *ws);
void *evas_swapper_buffer_map(Wl_Swapper *ws, int *w, int *h);
void evas_swapper_buffer_unmap(Wl_Swapper *ws);
int evas_swapper_buffer_state_get(Wl_Swapper *ws);
void evas_swapper_buffer_idle_flush(Wl_Swapper *ws);
int evas_buffer_manager_init(void);
int evas_buffer_manager_shutdown(void);
Eina_Bool evas_buffer_manager_open(int fd);
void evas_buffer_manager_close(void);
#endif

View File

@ -1,45 +0,0 @@
#include "evas_common_private.h"
#include "evas_private.h"
#include "evas_engine.h"
Outbuf *
evas_outbuf_setup(int w, int h, unsigned int rotation, unsigned int depth, Eina_Bool alpha)
{
Outbuf *ob;
/* try to allocate space for out outbuf structure */
if (!(ob = calloc(1, sizeof(Outbuf))))
return NULL;
/* set some default outbuf properties */
ob->w = w;
ob->h = h;
ob->rotation = rotation;
ob->depth = depth;
ob->destination_alpha = alpha;
/* return the allocated outbuf structure */
return ob;
}
void
evas_outbuf_free(Outbuf *ob)
{
/* free the allocated outbuf structure */
free(ob);
}
void
evas_outbuf_reconfigure(Outbuf *ob, int w, int h, unsigned int rotation, unsigned int depth, Eina_Bool alpha)
{
/* check for changes */
if ((ob->w == w) && (ob->h == h) && (ob->destination_alpha == alpha) &&
(ob->rotation == rotation) && (ob->depth == depth)) return;
/* set new outbuf properties */
ob->w = w;
ob->h = h;
ob->rotation = rotation;
ob->depth = depth;
ob->destination_alpha = alpha;
}

View File

@ -0,0 +1,522 @@
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
//#include <sys/mman.h>
#ifdef EVAS_CSERVE2
# include "evas_cs2_private.h"
#endif
#include "evas_engine.h"
#define RED_MASK 0x00ff0000
#define GREEN_MASK 0x0000ff00
#define BLUE_MASK 0x000000ff
/* local function prototypes */
Outbuf *
evas_swapbuf_setup(int w, int h, unsigned int rotation, Outbuf_Depth depth, Eina_Bool alpha, int fd)
{
Outbuf *ob = NULL;
LOGFN(__FILE__, __LINE__, __FUNCTION__);
/* try to allocate a new Outbuf */
if (!(ob = calloc(1, sizeof(Outbuf))))
return NULL;
/* set some properties */
ob->w = w;
ob->h = h;
ob->rotation = rotation;
ob->depth = depth;
ob->priv.destination_alpha = alpha;
ob->priv.fd = fd;
if ((ob->rotation == 0) || (ob->rotation == 180))
{
ob->priv.swapper =
evas_swapper_setup(0, 0, w, h, depth, alpha, fd);
}
else if ((ob->rotation == 90) || (ob->rotation == 270))
{
ob->priv.swapper =
evas_swapper_setup(0, 0, h, w, depth, alpha, fd);
}
/* check that a swapper was created */
if (!ob->priv.swapper)
{
/* free the Outbuf structure allocation */
free(ob);
return NULL;
}
/* set step size of regions array */
eina_array_step_set(&ob->priv.onebuf_regions, sizeof(Eina_Array), 8);
/* return allocated Outbuf */
return ob;
}
void
evas_swapbuf_free(Outbuf *ob)
{
LOGFN(__FILE__, __LINE__, __FUNCTION__);
/* check for valid output buffer */
if (!ob) return;
/* flush the output buffer */
evas_swapbuf_flush(ob);
evas_swapbuf_idle_flush(ob);
evas_swapper_free(ob->priv.swapper);
eina_array_flush(&ob->priv.onebuf_regions);
/* free the allocated structure */
free(ob);
}
void
evas_swapbuf_reconfigure(Outbuf *ob, int x, int y, int w, int h, unsigned int rotation, Outbuf_Depth depth, Eina_Bool alpha)
{
LOGFN(__FILE__, __LINE__, __FUNCTION__);
/* check for valid output buffer */
if (!ob) return;
/* check that something was actually changed */
if ((ob->w == w) && (ob->h == h) &&
(ob->rotation == rotation) && (ob->depth == depth))
return;
/* set some properties */
ob->w = w;
ob->h = h;
ob->rotation = rotation;
ob->depth = depth;
ob->priv.destination_alpha = alpha;
/* check for valid swapper */
if (ob->priv.swapper)
{
if ((ob->rotation == 0) || (ob->rotation == 180))
ob->priv.swapper = evas_swapper_reconfigure(ob->priv.swapper,
x, y, w, h, depth,
alpha);
else if ((ob->rotation == 90) || (ob->rotation == 270))
ob->priv.swapper = evas_swapper_reconfigure(ob->priv.swapper,
x, y, h, w, depth,
alpha);
return;
}
/* create new swapper */
if ((ob->rotation == 0) || (ob->rotation == 180))
{
ob->priv.swapper =
evas_swapper_setup(x, y, w, h, depth, alpha, ob->priv.fd);
}
else if ((ob->rotation == 90) || (ob->rotation == 270))
{
ob->priv.swapper =
evas_swapper_setup(x, y, h, w, depth, alpha, ob->priv.fd);
}
}
RGBA_Image *
evas_swapbuf_update_region_new(Outbuf *ob, int x, int y, int w, int h, int *cx, int *cy, int *cw, int *ch)
{
RGBA_Image *img;
Eina_Rectangle *rect;
LOGFN(__FILE__, __LINE__, __FUNCTION__);
RECTS_CLIP_TO_RECT(x, y, w, h, 0, 0, ob->w, ob->h);
if ((w <= 0) || (h <= 0)) return NULL;
if (ob->rotation == 0)
{
if (!(img = ob->priv.onebuf))
{
int bpl = 0;
int bw = 0, bh = 0;
void *data;
if (!(data = evas_swapper_buffer_map(ob->priv.swapper, &bw, &bh)))
{
ERR("NO BUFFER DATA !!");
return NULL;
}
bpl = (bw * sizeof(int));
#ifdef EVAS_CSERVE2
if (evas_cserve2_use_get())
img = (RGBA_Image *)evas_cache2_image_data(evas_common_image_cache2_get(),
bpl / sizeof(int), bh,
data,
ob->priv.destination_alpha,
EVAS_COLORSPACE_ARGB8888);
else
#endif
img = (RGBA_Image *)evas_cache_image_data(evas_common_image_cache_get(),
bpl / sizeof(int), bh,
data,
ob->priv.destination_alpha,
EVAS_COLORSPACE_ARGB8888);
ob->priv.onebuf = img;
if (!img) return NULL;
}
if (!(rect = eina_rectangle_new(x, y, w, h)))
return NULL;
if (!eina_array_push(&ob->priv.onebuf_regions, rect))
{
#ifdef EVAS_CSERVE2
if (evas_cserve2_use_get())
evas_cache2_image_close(&img->cache_entry);
else
#endif
evas_cache_image_drop(&img->cache_entry);
eina_rectangle_free(rect);
return NULL;
}
/* clip the region to the onebuf region */
if (cx) *cx = x;
if (cy) *cy = y;
if (cw) *cw = w;
if (ch) *ch = h;
return img;
}
else
{
if (!(rect = eina_rectangle_new(x, y, w, h)))
return NULL;
#ifdef EVAS_CSERVE2
if (evas_cserve2_use_get())
img = (RGBA_Image *)evas_cache2_image_empty(evas_common_image_cache2_get());
else
#endif
img = (RGBA_Image *)evas_cache_image_empty(evas_common_image_cache_get());
if (!img)
{
eina_rectangle_free(rect);
return NULL;
}
img->cache_entry.flags.alpha |= ob->priv.destination_alpha;
#ifdef EVAS_CSERVE2
if (evas_cserve2_use_get())
evas_cache2_image_surface_alloc(&img->cache_entry, w, h);
else
#endif
evas_cache_image_surface_alloc(&img->cache_entry, w, h);
img->extended_info = rect;
ob->priv.pending_writes =
eina_list_append(ob->priv.pending_writes, img);
if (cx) *cx = 0;
if (cy) *cy = 0;
if (cw) *cw = w;
if (ch) *ch = h;
return img;
}
return NULL;
}
void
evas_swapbuf_update_region_push(Outbuf *ob, RGBA_Image *update, int x, int y, int w, int h)
{
Gfx_Func_Convert func = NULL;
Eina_Rectangle rect = {0, 0, 0, 0}, pr;
DATA32 *src;
DATA8 *dst;
int depth = 32, bpp = 0, bpl = 0, wid = 0;
int ww = 0, hh = 0;
int rx = 0, ry = 0;
LOGFN(__FILE__, __LINE__, __FUNCTION__);
/* check for valid output buffer */
if (!ob) return;
/* check for pending writes */
if (!ob->priv.pending_writes) return;
if ((ob->rotation == 0) || (ob->rotation == 180))
{
func =
evas_common_convert_func_get(0, w, h, depth,
RED_MASK, GREEN_MASK, BLUE_MASK,
PAL_MODE_NONE, ob->rotation);
}
else if ((ob->rotation == 90) || (ob->rotation == 270))
{
func =
evas_common_convert_func_get(0, h, w, depth,
RED_MASK, GREEN_MASK, BLUE_MASK,
PAL_MODE_NONE, ob->rotation);
}
/* make sure we have a valid convert function */
if (!func) return;
/* based on rotation, set rectangle position */
if (ob->rotation == 0)
{
rect.x = x;
rect.y = y;
}
else if (ob->rotation == 90)
{
rect.x = y;
rect.y = (ob->w - x - w);
}
else if (ob->rotation == 180)
{
rect.x = (ob->w - x - w);
rect.y = (ob->h - y - h);
}
else if (ob->rotation == 270)
{
rect.x = (ob->h - y - h);
rect.y = x;
}
/* based on rotation, set rectangle size */
if ((ob->rotation == 0) || (ob->rotation == 180))
{
rect.w = w;
rect.h = h;
}
else if ((ob->rotation == 90) || (ob->rotation == 270))
{
rect.w = h;
rect.h = w;
}
/* check for valid update image data */
if (!(src = update->image.data)) return;
bpp = depth / 8;
if (bpp <= 0) return;
/* check for valid desination data */
if (!(dst = evas_swapper_buffer_map(ob->priv.swapper, &ww, &hh))) return;
bpl = (ww * sizeof(int));
if (ob->rotation == 0)
{
RECTS_CLIP_TO_RECT(rect.x, rect.y, rect.w, rect.h, 0, 0, ww, hh);
dst += (bpl * rect.y) + (rect.x + bpp);
w -= rx;
}
else if (ob->rotation == 180)
{
pr = rect;
RECTS_CLIP_TO_RECT(rect.x, rect.y, rect.w, rect.h, 0, 0, ww, hh);
rx = pr.w - rect.w;
ry = pr.h - rect.h;
src += (update->cache_entry.w * ry) + rx;
w -= rx;
}
else if (ob->rotation == 90)
{
pr = rect;
RECTS_CLIP_TO_RECT(rect.x, rect.y, rect.w, rect.h, 0, 0, ww, hh);
rx = pr.w - rect.w; ry = pr.h - rect.h;
src += ry;
w -= ry;
}
else if (ob->rotation == 270)
{
pr = rect;
RECTS_CLIP_TO_RECT(rect.x, rect.y, rect.w, rect.h, 0, 0, ww, hh);
rx = pr.w - rect.w; ry = pr.h - rect.h;
src += (update->cache_entry.w * rx);
w -= ry;
}
if ((rect.w <= 0) || (rect.h <= 0)) return;
wid = bpl / bpp;
dst += (bpl * rect.y) + (rect.x * bpp);
func(src, dst, (update->cache_entry.w - w), (wid - rect.w),
rect.w, rect.h, x + rx, y + ry, NULL);
}
void
evas_swapbuf_update_region_free(Outbuf *ob EINA_UNUSED, RGBA_Image *update EINA_UNUSED)
{
/* NB: nothing to do, they are cleaned up on flush */
}
void
evas_swapbuf_flush(Outbuf *ob)
{
Eina_Rectangle *rects;
RGBA_Image *img;
unsigned int n = 0, i = 0;
LOGFN(__FILE__, __LINE__, __FUNCTION__);
/* check for valid output buffer */
if (!ob) return;
/* check for pending writes */
if (!ob->priv.pending_writes)
{
Eina_Rectangle *rect;
Eina_Array_Iterator it;
/* get number of buffer regions */
n = eina_array_count_get(&ob->priv.onebuf_regions);
if (n == 0) return;
/* allocate rectangles */
if (!(rects = alloca(n * sizeof(Eina_Rectangle)))) return;
/* loop the buffer regions and assign to rects */
EINA_ARRAY_ITER_NEXT(&ob->priv.onebuf_regions, i, rect, it)
rects[i] = *rect;
/* unmap the buffer */
evas_swapper_buffer_unmap(ob->priv.swapper);
/* force a buffer swap */
evas_swapper_swap(ob->priv.swapper, rects, n);
/* clean array */
eina_array_clean(&ob->priv.onebuf_regions);
img = ob->priv.onebuf;
ob->priv.onebuf = NULL;
if (img)
{
#ifdef EVAS_CSERVE2
if (evas_cserve2_use_get())
evas_cache2_image_close(&img->cache_entry);
else
#endif
evas_cache_image_drop(&img->cache_entry);
}
}
else
{
/* get number of pending writes */
n = eina_list_count(ob->priv.pending_writes);
if (n == 0) return;
/* allocate rectangles */
if (!(rects = alloca(n * sizeof(Eina_Rectangle)))) return;
/* loop the pending writes */
EINA_LIST_FREE(ob->priv.pending_writes, img)
{
Eina_Rectangle *rect;
int x = 0, y = 0, w = 0, h = 0;
if (!(rect = img->extended_info)) continue;
x = rect->x; y = rect->y; w = rect->w; h = rect->h;
/* based on rotation, set rectangle position */
if (ob->rotation == 0)
{
rects[i].x = x;
rects[i].y = y;
}
else if (ob->rotation == 90)
{
rects[i].x = y;
rects[i].y = (ob->w - x - w);
}
else if (ob->rotation == 180)
{
rects[i].x = (ob->w - x - w);
rects[i].y = (ob->h - y - h);
}
else if (ob->rotation == 270)
{
rects[i].x = (ob->h - y - h);
rects[i].y = x;
}
/* based on rotation, set rectangle size */
if ((ob->rotation == 0) || (ob->rotation == 180))
{
rects[i].w = w;
rects[i].h = h;
}
else if ((ob->rotation == 90) || (ob->rotation == 270))
{
rects[i].w = h;
rects[i].h = w;
}
eina_rectangle_free(rect);
#ifdef EVAS_CSERVE2
if (evas_cserve2_use_get())
evas_cache2_image_close(&img->cache_entry);
else
#endif
evas_cache_image_drop(&img->cache_entry);
i++;
}
/* unmap the buffer */
evas_swapper_buffer_unmap(ob->priv.swapper);
/* force a buffer swap */
evas_swapper_swap(ob->priv.swapper, rects, n);
}
}
void
evas_swapbuf_idle_flush(Outbuf *ob EINA_UNUSED)
{
// LOGFN(__FILE__, __LINE__, __FUNCTION__);
/* check for valid output buffer */
/* if (!ob) return; */
/* check for valid swapper */
/* if (!ob->priv.swapper) return; */
/* tell the swapper to release any buffers that have been rendered */
/* evas_swapper_buffer_idle_flush(ob->priv.swapper); */
}
int
evas_swapbuf_state_get(Outbuf *ob)
{
int mode = 0;
LOGFN(__FILE__, __LINE__, __FUNCTION__);
if (!ob->priv.swapper) return MODE_FULL;
mode = evas_swapper_buffer_state_get(ob->priv.swapper);
return mode;
}
/* local functions */

View File

@ -0,0 +1,401 @@
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <sys/mman.h>
#ifdef EVAS_CSERVE2
# include "evas_cs2_private.h"
#endif
#include "evas_engine.h"
/* local structures */
typedef struct _Wl_Buffer Wl_Buffer;
struct _Wl_Buffer
{
Wl_Swapper *ws;
int w, h;
void *data;
int offset;
unsigned int id, hdl;
size_t size;
Eina_Bool valid : 1;
};
struct _Wl_Swapper
{
Wl_Buffer buff[3];
Wl_Buffer *buffer_sent;
int in_use, drm_fd;
int dx, dy, w, h, depth;
int buff_cur, buff_num;
/* void *data; */
Eina_Bool alpha : 1;
Eina_Bool mapped : 1;
Eina_Bool delete_me : 1;
};
/* local function prototypes */
/* static Eina_Bool _evas_swapper_shm_pool_new(Wl_Swapper *ws); */
/* static void _evas_swapper_shm_pool_free(Wl_Swapper *ws); */
static Eina_Bool _evas_swapper_buffer_new(Wl_Swapper *ws, Wl_Buffer *wb);
static void _evas_swapper_buffer_free(Wl_Swapper *ws, Wl_Buffer *wb);
static void _evas_swapper_buffer_put(Wl_Swapper *ws, Wl_Buffer *wb, Eina_Rectangle *rects, unsigned int count);
/* local variables */
Wl_Swapper *
evas_swapper_setup(int dx, int dy, int w, int h, Outbuf_Depth depth, Eina_Bool alpha, int fd)
{
Wl_Swapper *ws;
int i = 0;
char *num_buffers;
LOGFN(__FILE__, __LINE__, __FUNCTION__);
/* try to allocate a new swapper */
if (!(ws = calloc(1, sizeof(Wl_Swapper))))
return NULL;
/* set some properties */
ws->dx = dx;
ws->dy = dy;
ws->w = w;
ws->h = h;
ws->depth = depth;
ws->alpha = alpha;
ws->drm_fd = fd;
/* double buffer by default */
ws->buff_num = 2;
/* check for buffer override number */
if ((num_buffers = getenv("EVAS_DRM_BUFFERS")))
{
int num = 0;
num = atoi(num_buffers);
if (num <= 0) num = 1;
if (num > 3) num = 3;
ws->buff_num = num;
}
for (i = 0; i < ws->buff_num; i++)
{
/* try to create new internal Wl_Buffer */
if (!_evas_swapper_buffer_new(ws, &(ws->buff[i])))
{
/* failed to create buffer. free the swapper */
ERR("Failed to create new buffer");
evas_swapper_free(ws);
return NULL;
}
}
/* return allocated swapper */
return ws;
}
Wl_Swapper *
evas_swapper_reconfigure(Wl_Swapper *ws, int dx, int dy, int w, int h, Outbuf_Depth depth, Eina_Bool alpha)
{
int i = 0;
LOGFN(__FILE__, __LINE__, __FUNCTION__);
if (!ws) return NULL;
/* loop the swapper's buffers and free them */
for (i = 0; i < ws->buff_num; i++)
_evas_swapper_buffer_free(ws, &(ws->buff[i]));
ws->dx += dx;
ws->dy += dy;
ws->w = w;
ws->h = h;
ws->depth = depth;
ws->alpha = alpha;
for (i = 0; i < ws->buff_num; i++)
{
/* try to create new internal Wl_Buffer */
if (!_evas_swapper_buffer_new(ws, &(ws->buff[i])))
{
ERR("failed to create wl_buffer. free the swapper.");
evas_swapper_free(ws);
return NULL;
}
}
/* return reconfigured swapper */
return ws;
}
void
evas_swapper_swap(Wl_Swapper *ws, Eina_Rectangle *rects, unsigned int count)
{
int n = 0;
LOGFN(__FILE__, __LINE__, __FUNCTION__);
/* check for valid swapper */
if (!ws) return;
n = ws->buff_cur;
_evas_swapper_buffer_put(ws, &(ws->buff[n]), rects, count);
ws->buff[n].valid = EINA_TRUE;
ws->in_use++;
ws->buff_cur = (ws->buff_cur + 1) % ws->buff_num;
}
void
evas_swapper_free(Wl_Swapper *ws)
{
int i = 0;
LOGFN(__FILE__, __LINE__, __FUNCTION__);
/* check for valid swapper */
if (!ws) return;
/* loop the swapper's buffers and free them */
for (i = 0; i < ws->buff_num; i++)
_evas_swapper_buffer_free(ws, &(ws->buff[i]));
if (ws->in_use)
{
ws->delete_me = EINA_TRUE;
return;
}
/* free the allocated structure */
free(ws);
}
void *
evas_swapper_buffer_map(Wl_Swapper *ws, int *w, int *h)
{
LOGFN(__FILE__, __LINE__, __FUNCTION__);
/* check for valid swapper */
if (!ws) return NULL;
/* set mapped property */
ws->mapped = EINA_TRUE;
if (w) *w = ws->w;
if (h) *h = ws->h;
/* return wl_buffer data */
return ws->buff[ws->buff_cur].data;
}
void
evas_swapper_buffer_unmap(Wl_Swapper *ws)
{
LOGFN(__FILE__, __LINE__, __FUNCTION__);
/* check for valid swapper */
if (!ws) return;
ws->mapped = EINA_FALSE;
}
int
evas_swapper_buffer_state_get(Wl_Swapper *ws)
{
int i = 0, n = 0, count = 0;
LOGFN(__FILE__, __LINE__, __FUNCTION__);
for (i = 0; i < ws->buff_num; i++)
{
n = (ws->buff_num + ws->buff_cur - (i)) % ws->buff_num;
if (ws->buff[n].valid) count++;
else break;
}
if (count == ws->buff_num)
{
if (count == 1) return MODE_COPY;
else if (count == 2) return MODE_DOUBLE;
else if (count == 3) return MODE_TRIPLE;
}
return MODE_FULL;
}
void
evas_swapper_buffer_idle_flush(Wl_Swapper *ws)
{
int i = 0;
LOGFN(__FILE__, __LINE__, __FUNCTION__);
/* check for valid swapper */
if (!ws) return;
/* loop the swapper's buffers and free them */
for (i = 0; i < ws->buff_num; i++)
{
Wl_Buffer *wb = NULL;
/* try to get out Wl_Buffer struct */
if (!(wb = (&(ws->buff[i])))) continue;
/* if this buffer is not valid, then unmap data */
if (!wb->valid) _evas_swapper_buffer_free(ws, wb);
}
}
/* local functions */
static Eina_Bool
_evas_swapper_buffer_new(Wl_Swapper *ws, Wl_Buffer *wb)
{
/* struct drm_mode_create_dumb carg; */
/* struct drm_mode_map_dumb marg; */
/* struct drm_mode_destroy_dumb darg; */
int ret;
LOGFN(__FILE__, __LINE__, __FUNCTION__);
wb->w = ws->w;
wb->h = ws->h;
/* NB: Create drm Dumb FB for software rendering */
/* memset(&carg, 0, sizeof(struct drm_mode_create_dumb)); */
/* carg.bpp = 32; */
/* carg.width = wb->w; */
/* carg.height = wb->h; */
/* ret = drmIoctl(ws->drm_fd, DRM_IOCTL_MODE_CREATE_DUMB, &carg); */
/* if (ret < 0) */
/* { */
/* ERR("Failed to create dumb buffer: %m"); */
/* return EINA_FALSE; */
/* } */
/* ret = drmModeAddFB(ws->drm_fd, wb->w, wb->h, 24, 32, */
/* carg.pitch, carg.handle, &wb->id); */
/* if (ret) */
/* { */
/* ERR("Failed to add fb: %m"); */
/* goto err; */
/* } */
/* memset(&marg, 0, sizeof(struct drm_mode_map_dumb)); */
/* marg.handle = carg.handle; */
/* ret = drmIoctl(ws->drm_fd, DRM_IOCTL_MODE_MAP_DUMB, &marg); */
/* if (ret) */
/* { */
/* ERR("Failed to Map fb: %m"); */
/* goto err_map; */
/* } */
/* wb->data = mmap(0, carg.size, PROT_WRITE | PROT_READ, MAP_SHARED, */
/* ws->drm_fd, marg.offset); */
/* memset(wb->data, 0, carg.size); */
/* wb->hdl = marg.handle; */
wb->ws = ws;
/* return allocated buffer */
return EINA_TRUE;
/* err_map: */
/* drmModeRmFB(ws->drm_fd, wb->id); */
/* err: */
/* memset(&darg, 0, sizeof(darg)); */
/* darg.handle = carg.handle; */
/* drmIoctl(ws->drm_fd, DRM_IOCTL_MODE_DESTROY_DUMB, &darg); */
/* return EINA_FALSE; */
}
static void
_evas_swapper_buffer_free(Wl_Swapper *ws, Wl_Buffer *wb)
{
/* struct drm_mode_destroy_dumb darg; */
LOGFN(__FILE__, __LINE__, __FUNCTION__);
/* check for valid buffer */
if ((!wb) || (wb->valid)) return;
/* unmap the buffer data */
/* if (wb->data) munmap(wb->data, wb->size); */
/* wb->data = NULL; */
/* kill the wl_buffer */
/* if (wb->id) drmModeRmFB(ws->drm_fd, wb->id); */
/* if (wb->buffer) wl_buffer_destroy(wb->buffer); */
/* wb->buffer = NULL; */
/* memset(&darg, 0, sizeof(darg)); */
/* darg.handle = wb->hdl; */
/* drmIoctl(ws->drm_fd, DRM_IOCTL_MODE_DESTROY_DUMB, &darg); */
}
static void
_evas_swapper_buffer_put(Wl_Swapper *ws, Wl_Buffer *wb, Eina_Rectangle *rects, unsigned int count)
{
Eina_Rectangle *rect;
LOGFN(__FILE__, __LINE__, __FUNCTION__);
/* check for valid swapper */
if (!ws) return;
/* make sure swapper has a surface */
/* if (!ws->surface) return; */
/* check for valid buffer */
if (!wb) return;
/* make sure buffer has mapped data */
if ((!wb->data))
{
/* call function to mmap buffer data */
if (!_evas_swapper_buffer_new(ws, wb))
return;
}
rect = eina_rectangle_new(0, 0, 0, 0);
if ((rects) && (count > 0))
{
unsigned int i = 0;
for (i = 0; i < count; i++)
eina_rectangle_union(rect, &rects[i]);
}
else
{
Eina_Rectangle r;
r.x = 0; r.y = 0;
r.w = wb->w; r.h = wb->h;
eina_rectangle_union(rect, &r);
}
/* surface attach */
if (ws->buffer_sent != wb)
{
DBG("Send Buffer !!");
/* wl_surface_attach(ws->surface, wb->buffer, ws->dx, ws->dy); */
ws->dx = 0;
ws->dy = 0;
ws->buffer_sent = wb;
}
/* wl_surface_damage(ws->surface, rect->x, rect->y, rect->w, rect->h); */
/* surface commit */
/* wl_surface_commit(ws->surface); */
}