use std::ffi::{ c_int, c_ulong, c_void };
use std::ptr;
use glow::HasContext;
use gobject_sys::*;
use wpe_platform_sys::*;
const PROT_READ: c_int = 1;
const MAP_SHARED: c_int = 1;
const DMA_BUF_IOCTL_SYNC: c_ulong = 0x4008_6200;
const DMA_BUF_SYNC_READ: u64 = 1;
const DMA_BUF_SYNC_START: u64 = 0;
const DMA_BUF_SYNC_END: u64 = 4;
const DRM_FORMAT_XRGB8888: u32 = 0x3432_5258;
const DRM_FORMAT_ARGB8888: u32 = 0x3432_5241;
const DRM_FORMAT_XBGR8888: u32 = 0x3432_4258;
const DRM_FORMAT_ABGR8888: u32 = 0x3432_4241;
const DRM_FORMAT_MOD_LINEAR: u64 = 0;
const DRM_FORMAT_MOD_INVALID: u64 = 0x00ff_ffff_ffff_ffff;
#[ repr( C ) ]
struct DmaBufSync
{
flags: u64,
}
extern "C"
{
fn mmap( addr: *mut c_void, len: usize, prot: c_int, flags: c_int, fd: c_int, offset: i64 ) -> *mut c_void;
fn munmap( addr: *mut c_void, len: usize ) -> c_int;
fn ioctl( fd: c_int, request: c_ulong, ... ) -> c_int;
}
pub fn wanted( gl: &glow::Context ) -> bool
{
if std::env::var_os( "LTK_WEBKIT_COPY" ).is_some()
{
return true;
}
let renderer = unsafe { gl.get_parameter_string( glow::RENDERER ) };
renderer.contains( "llvmpipe" ) || renderer.contains( "softpipe" )
}
pub fn upload(
gl: &glow::Context,
texture: &mut Option<glow::Texture>,
size: &mut ( i32, i32 ),
buffer: *mut WPEBuffer,
) -> Result<(), String>
{
let is_dmabuf = unsafe
{
g_type_check_instance_is_a( buffer as *mut GTypeInstance, wpe_buffer_dma_buf_get_type() )
} != 0;
if !is_dmabuf
{
return Err( "buffer is not WPEBufferDMABuf".into() );
}
let dmabuf = buffer as *mut WPEBufferDMABuf;
let ( width, height, fourcc, planes, modifier, fd, offset, pitch ) = unsafe
{
(
wpe_buffer_get_width( buffer ) as i32,
wpe_buffer_get_height( buffer ) as i32,
wpe_buffer_dma_buf_get_format( dmabuf ) as u32,
wpe_buffer_dma_buf_get_n_planes( dmabuf ),
wpe_buffer_dma_buf_get_modifier( dmabuf ) as u64,
wpe_buffer_dma_buf_get_fd( dmabuf, 0 ) as c_int,
wpe_buffer_dma_buf_get_offset( dmabuf, 0 ) as usize,
wpe_buffer_dma_buf_get_stride( dmabuf, 0 ) as usize,
)
};
super::dbg_log( &format!(
"dmabuf {}x{} fourcc=0x{:08x} modifier={:#x} planes={} pitch={}",
width, height, fourcc, modifier, planes, pitch,
) );
let ( swap_rb, opaque ) = match fourcc
{
DRM_FORMAT_ARGB8888 => ( true, false ),
DRM_FORMAT_XRGB8888 => ( true, true ),
DRM_FORMAT_ABGR8888 => ( false, false ),
DRM_FORMAT_XBGR8888 => ( false, true ),
_ => return Err( format!( "unsupported fourcc 0x{:08x}", fourcc ) ),
};
if planes != 1
{
return Err( format!( "unsupported plane count {}", planes ) );
}
if modifier != DRM_FORMAT_MOD_LINEAR && modifier != DRM_FORMAT_MOD_INVALID && modifier != u64::MAX
{
return Err( format!( "unsupported modifier {:#x}", modifier ) );
}
if width <= 0 || height <= 0 || pitch % 4 != 0 || pitch < width as usize * 4
{
return Err( format!( "unsupported geometry {}x{} pitch {}", width, height, pitch ) );
}
let rows = pitch * height as usize;
let len = offset + rows;
let map = unsafe { mmap( ptr::null_mut(), len, PROT_READ, MAP_SHARED, fd, 0 ) };
if map as isize == -1
{
return Err( format!( "mmap failed: {}", std::io::Error::last_os_error() ) );
}
let sync = | flags: u64 |
{
let arg = DmaBufSync { flags };
unsafe { ioctl( fd, DMA_BUF_IOCTL_SYNC, &arg as *const DmaBufSync ); }
};
sync( DMA_BUF_SYNC_START | DMA_BUF_SYNC_READ );
let pixels = unsafe { std::slice::from_raw_parts( ( map as *const u8 ).add( offset ), rows ) };
let result = unsafe { upload_pixels( gl, texture, size, pixels, width, height, pitch, swap_rb, opaque ) };
sync( DMA_BUF_SYNC_END | DMA_BUF_SYNC_READ );
unsafe { munmap( map, len ); }
result
}
#[ allow( clippy::too_many_arguments ) ]
unsafe fn upload_pixels(
gl: &glow::Context,
texture: &mut Option<glow::Texture>,
size: &mut ( i32, i32 ),
pixels: &[u8],
width: i32,
height: i32,
pitch: usize,
swap_rb: bool,
opaque: bool,
) -> Result<(), String>
{
let tex = match *texture
{
Some( tex ) => tex,
None =>
{
let tex = unsafe { gl.create_texture() }?;
let swizzle = [
if swap_rb { glow::BLUE } else { glow::RED },
if swap_rb { glow::RED } else { glow::BLUE },
if opaque { glow::ONE } else { glow::ALPHA },
];
unsafe
{
gl.bind_texture( glow::TEXTURE_2D, Some( tex ) );
gl.tex_parameter_i32( glow::TEXTURE_2D, glow::TEXTURE_MIN_FILTER, glow::LINEAR as i32 );
gl.tex_parameter_i32( glow::TEXTURE_2D, glow::TEXTURE_MAG_FILTER, glow::LINEAR as i32 );
gl.tex_parameter_i32( glow::TEXTURE_2D, glow::TEXTURE_WRAP_S, glow::CLAMP_TO_EDGE as i32 );
gl.tex_parameter_i32( glow::TEXTURE_2D, glow::TEXTURE_WRAP_T, glow::CLAMP_TO_EDGE as i32 );
gl.tex_parameter_i32( glow::TEXTURE_2D, glow::TEXTURE_SWIZZLE_R, swizzle[ 0 ] as i32 );
gl.tex_parameter_i32( glow::TEXTURE_2D, glow::TEXTURE_SWIZZLE_B, swizzle[ 1 ] as i32 );
gl.tex_parameter_i32( glow::TEXTURE_2D, glow::TEXTURE_SWIZZLE_A, swizzle[ 2 ] as i32 );
}
*texture = Some( tex );
*size = ( 0, 0 );
tex
}
};
unsafe
{
gl.bind_texture( glow::TEXTURE_2D, Some( tex ) );
gl.pixel_store_i32( glow::UNPACK_ALIGNMENT, 4 );
gl.pixel_store_i32( glow::UNPACK_ROW_LENGTH, ( pitch / 4 ) as i32 );
if *size == ( width, height )
{
gl.tex_sub_image_2d(
glow::TEXTURE_2D, 0, 0, 0, width, height,
glow::RGBA, glow::UNSIGNED_BYTE, glow::PixelUnpackData::Slice( Some( pixels ) ),
);
} else {
gl.tex_image_2d(
glow::TEXTURE_2D, 0, glow::RGBA8 as i32, width, height, 0,
glow::RGBA, glow::UNSIGNED_BYTE, glow::PixelUnpackData::Slice( Some( pixels ) ),
);
*size = ( width, height );
}
gl.pixel_store_i32( glow::UNPACK_ROW_LENGTH, 0 );
}
Ok( () )
}