static int vidioc_streamoff(struct file *file, void *__fh, enum v4l2_buf_type type)
{
	struct saa7146_fh *fh = __fh;
	struct saa7146_dev *dev = fh->dev;
	struct saa7146_vv *vv = dev->vv_data;
	int err;

	DEB_D("VIDIOC_STREAMOFF, type:%d\n", type);

	/* ugly: we need to copy some checks from video_end(),
	   because videobuf_streamoff() relies on the capture running.
	   check and fix this */
	if ((vv->video_status & STATUS_CAPTURE) != STATUS_CAPTURE) {
		DEB_S("not capturing\n");
		return 0;
	}

	if (vv->video_fh != fh) {
		DEB_S("capturing, but in another open\n");
		return -EBUSY;
	}

	err = -EINVAL;
	if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
		err = videobuf_streamoff(&fh->video_q);
	else if (type == V4L2_BUF_TYPE_VBI_CAPTURE)
		err = videobuf_streamoff(&fh->vbi_q);
	if (0 != err) {
		DEB_D("warning: videobuf_streamoff() failed\n");
		video_end(fh, file);
	} else {
		err = video_end(fh, file);
	}
	return err;
}
Exemplo n.º 2
0
_Bool doevent(void)
{
    XEvent event;
    XNextEvent(display, &event);
    if(event.xany.window && event.xany.window != window) {
        if(event.type == ClientMessage) {
            XClientMessageEvent *ev = &event.xclient;
            if((Atom)event.xclient.data.l[0] == wm_delete_window) {
                if(ev->window == video_win[0]) {
                    video_end(0);
                    video_preview = 0;
                    toxvideo_postmessage(VIDEO_PREVIEW_END, 0, 0, NULL);
                    return 1;
                }

                int i;
                for(i = 0; i != countof(friend); i++) {
                    if(video_win[i + 1] == ev->window) {
                        FRIEND *f = &friend[i];
                        tox_postmessage(TOX_HANGUP, f->callid, 0, NULL);
                        break;
                    }
                }
                if(i == countof(friend)) {
                    debug("this should not happen\n");
                }
            }
        }
static ssize_t video_read(struct file *file, char __user *data, size_t count, loff_t *ppos)
{
	struct saa7146_fh *fh = file->private_data;
	struct saa7146_dev *dev = fh->dev;
	struct saa7146_vv *vv = dev->vv_data;
	ssize_t ret = 0;

	DEB_EE(("called.\n"));

	if ((vv->video_status & STATUS_CAPTURE) != 0) {
		/* fixme: should we allow read() captures while streaming capture? */
		if (vv->video_fh == fh) {
			DEB_S(("already capturing.\n"));
			return -EBUSY;
		}
		DEB_S(("already capturing in another open.\n"));
		return -EBUSY;
	}

	ret = video_begin(fh);
	if( 0 != ret) {
		goto out;
	}

	ret = videobuf_read_one(&fh->video_q , data, count, ppos,
				file->f_flags & O_NONBLOCK);
	if (ret != 0) {
		video_end(fh, file);
	} else {
		ret = video_end(fh, file);
	}
out:
	/* restart overlay if it was active before */
	if (vv->ov_suspend != NULL) {
		saa7146_start_preview(vv->ov_suspend);
		vv->ov_suspend = NULL;
	}

	return ret;
}
static void video_close(struct saa7146_dev *dev, struct file *file)
{
	struct saa7146_fh *fh = file->private_data;
	struct saa7146_vv *vv = dev->vv_data;
	struct videobuf_queue *q = &fh->video_q;

	if (IS_CAPTURE_ACTIVE(fh) != 0)
		video_end(fh, file);
	else if (IS_OVERLAY_ACTIVE(fh) != 0)
		saa7146_stop_preview(fh);

	videobuf_stop(q);
	/* hmm, why is this function declared void? */
}
Exemplo n.º 5
0
static void dropdown_video_onselect(uint16_t i, const DROPDOWN* dm)
{
    DROP_ELEMENT *e = &((DROP_ELEMENT*) dm->userdata)[i];
    void *handle = e->handle;
    if(!handle && video_preview) {
        // if no device is selected while previewing, close the preview window
        video_end(0);
        video_preview = 0;
    } else if((size_t)handle == 1) {
        desktopgrab(1);
        return;
    }
    postmessage_utoxav(VIDEO_SET, 0, 0, handle);
}
static void video_close(struct saa7146_dev *dev, struct file *file)
{
	struct saa7146_fh *fh = (struct saa7146_fh *)file->private_data;
	struct saa7146_vv *vv = dev->vv_data;
	int err;

	if (IS_CAPTURE_ACTIVE(fh) != 0) {
		err = video_end(fh, file);
	} else if (IS_OVERLAY_ACTIVE(fh) != 0) {
		err = saa7146_stop_preview(fh);
	}

	/* hmm, why is this function declared void? */
	/* return err */
}
Exemplo n.º 7
0
static void dropdown_video_onselect(uint16_t i, const DROPDOWN* dm)
{
    DROP_ELEMENT *e = &((DROP_ELEMENT*) dm->userdata)[i];
    void *handle = e->handle;
    uint16_t b = 0;
    if(!handle && video_preview) {
        video_end(0);
        video_preview = 0;
        b = 1; //tell video thread to stop preview too
    } else if((size_t)handle == 1) {
        desktopgrab(1);
        return;
    }

    toxvideo_postmessage(VIDEO_SET, b, 0, handle);
}
Exemplo n.º 8
0
static void video_close(struct saa7146_dev *dev, struct file *file)
{
	struct saa7146_fh *fh = (struct saa7146_fh *)file->private_data;
	struct saa7146_vv *vv = dev->vv_data;
	struct videobuf_queue *q = &fh->video_q;
	int err;

	if (IS_CAPTURE_ACTIVE(fh) != 0) {
		err = video_end(fh, file);
	} else if (IS_OVERLAY_ACTIVE(fh) != 0) {
		err = saa7146_stop_preview(fh);
	}

	// release all capture buffers
	mutex_lock(&q->lock);
	videobuf_read_stop(q);
	mutex_unlock(&q->lock);

	/* hmm, why is this function declared void? */
	/* return err */
}
int saa7146_video_do_ioctl(struct inode *inode, struct file *file, unsigned int cmd, void *arg)
{
	struct saa7146_fh *fh  = file->private_data;
	struct saa7146_dev *dev = fh->dev;
	struct saa7146_vv *vv = dev->vv_data;

	int err = 0, result = 0, ee = 0;

	struct saa7146_use_ops *ops;
	struct videobuf_queue *q;

	/* check if extension handles the command */
	for(ee = 0; dev->ext_vv_data->ioctls[ee].flags != 0; ee++) {
		if( cmd == dev->ext_vv_data->ioctls[ee].cmd )
			break;
	}

	if( 0 != (dev->ext_vv_data->ioctls[ee].flags & SAA7146_EXCLUSIVE) ) {
		DEB_D(("extension handles ioctl exclusive.\n"));
		result = dev->ext_vv_data->ioctl(fh, cmd, arg);
		return result;
	}
	if( 0 != (dev->ext_vv_data->ioctls[ee].flags & SAA7146_BEFORE) ) {
		DEB_D(("extension handles ioctl before.\n"));
		result = dev->ext_vv_data->ioctl(fh, cmd, arg);
		if( -EAGAIN != result ) {
			return result;
		}
	}

	/* fixme: add handle "after" case (is it still needed?) */

	switch (fh->type) {
	case V4L2_BUF_TYPE_VIDEO_CAPTURE: {
		ops = &saa7146_video_uops;
		q = &fh->video_q;
		break;
		}
	case V4L2_BUF_TYPE_VBI_CAPTURE: {
		ops = &saa7146_vbi_uops;
		q = &fh->vbi_q;
		break;
		}
	default:
		BUG();
		return 0;
	}

	switch (cmd) {
	case VIDIOC_QUERYCAP:
	{
		struct v4l2_capability *cap = arg;
		memset(cap,0,sizeof(*cap));

		DEB_EE(("VIDIOC_QUERYCAP\n"));

		strcpy(cap->driver, "saa7146 v4l2");
		strlcpy(cap->card, dev->ext->name, sizeof(cap->card));
		sprintf(cap->bus_info,"PCI:%s", pci_name(dev->pci));
		cap->version = SAA7146_VERSION_CODE;
		cap->capabilities =
			V4L2_CAP_VIDEO_CAPTURE |
			V4L2_CAP_VIDEO_OVERLAY |
			V4L2_CAP_READWRITE |
			V4L2_CAP_STREAMING;
		cap->capabilities |= dev->ext_vv_data->capabilities;
		return 0;
	}
	case VIDIOC_G_FBUF:
	{
		struct v4l2_framebuffer *fb = arg;

		DEB_EE(("VIDIOC_G_FBUF\n"));

		*fb = vv->ov_fb;
		fb->capability = V4L2_FBUF_CAP_LIST_CLIPPING;
		return 0;
	}
	case VIDIOC_S_FBUF:
	{
		struct v4l2_framebuffer *fb = arg;
		struct saa7146_format *fmt;

		DEB_EE(("VIDIOC_S_FBUF\n"));

		if(!capable(CAP_SYS_ADMIN) &&
		   !capable(CAP_SYS_RAWIO))
			return -EPERM;

		/* check args */
		fmt = format_by_fourcc(dev,fb->fmt.pixelformat);
		if (NULL == fmt) {
			return -EINVAL;
		}

		/* planar formats are not allowed for overlay video, clipping and video dma would clash */
		if (0 != (fmt->flags & FORMAT_IS_PLANAR)) {
			DEB_S(("planar pixelformat '%4.4s' not allowed for overlay\n",(char *)&fmt->pixelformat));
		}

		/* check if overlay is running */
		if (IS_OVERLAY_ACTIVE(fh) != 0) {
			if (vv->video_fh != fh) {
				DEB_D(("refusing to change framebuffer informations while overlay is active in another open.\n"));
				return -EBUSY;
			}
		}

		down(&dev->lock);

		/* ok, accept it */
		vv->ov_fb = *fb;
		vv->ov_fmt = fmt;
		if (0 == vv->ov_fb.fmt.bytesperline)
			vv->ov_fb.fmt.bytesperline =
				vv->ov_fb.fmt.width*fmt->depth/8;

		up(&dev->lock);

		return 0;
	}
	case VIDIOC_ENUM_FMT:
	{
		struct v4l2_fmtdesc *f = arg;
		int index;

		switch (f->type) {
		case V4L2_BUF_TYPE_VIDEO_CAPTURE:
		case V4L2_BUF_TYPE_VIDEO_OVERLAY: {
			index = f->index;
			if (index < 0 || index >= NUM_FORMATS) {
				return -EINVAL;
			}
			memset(f,0,sizeof(*f));
			f->index = index;
			strlcpy(f->description,formats[index].name,sizeof(f->description));
			f->pixelformat = formats[index].pixelformat;
			break;
		}
		default:
			return -EINVAL;
		}

		DEB_EE(("VIDIOC_ENUM_FMT: type:%d, index:%d\n",f->type,f->index));
		return 0;
	}
	case VIDIOC_QUERYCTRL:
	{
		const struct v4l2_queryctrl *ctrl;
		struct v4l2_queryctrl *c = arg;

		if ((c->id <  V4L2_CID_BASE ||
		     c->id >= V4L2_CID_LASTP1) &&
		    (c->id <  V4L2_CID_PRIVATE_BASE ||
		     c->id >= V4L2_CID_PRIVATE_LASTP1))
			return -EINVAL;

		ctrl = ctrl_by_id(c->id);
		if( NULL == ctrl ) {
			return -EINVAL;
/*
			c->flags = V4L2_CTRL_FLAG_DISABLED;
			return 0;
*/
		}

		DEB_EE(("VIDIOC_QUERYCTRL: id:%d\n",c->id));
		*c = *ctrl;
		return 0;
	}
	case VIDIOC_G_CTRL: {
		DEB_EE(("VIDIOC_G_CTRL\n"));
		return get_control(fh,arg);
	}
	case VIDIOC_S_CTRL:
	{
		DEB_EE(("VIDIOC_S_CTRL\n"));
		err = set_control(fh,arg);
		return err;
	}
	case VIDIOC_G_PARM:
	{
		struct v4l2_streamparm *parm = arg;
		if( parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ) {
			return -EINVAL;
		}
		memset(&parm->parm.capture,0,sizeof(struct v4l2_captureparm));
		parm->parm.capture.readbuffers = 1;
		// fixme: only for PAL!
		parm->parm.capture.timeperframe.numerator = 1;
		parm->parm.capture.timeperframe.denominator = 25;
		return 0;
	}
	case VIDIOC_G_FMT:
	{
		struct v4l2_format *f = arg;
		DEB_EE(("VIDIOC_G_FMT\n"));
		return g_fmt(fh,f);
	}
	case VIDIOC_S_FMT:
	{
		struct v4l2_format *f = arg;
		DEB_EE(("VIDIOC_S_FMT\n"));
		return s_fmt(fh,f);
	}
	case VIDIOC_TRY_FMT:
	{
		struct v4l2_format *f = arg;
		DEB_EE(("VIDIOC_TRY_FMT\n"));
		return try_fmt(fh,f);
	}
	case VIDIOC_G_STD:
	{
		v4l2_std_id *id = arg;
		DEB_EE(("VIDIOC_G_STD\n"));
		*id = vv->standard->id;
		return 0;
	}
	/* the saa7146 supfhrts (used in conjunction with the saa7111a for example)
	   PAL / NTSC / SECAM. if your hardware does not (or does more)
	   -- override this function in your extension */
	case VIDIOC_ENUMSTD:
	{
		struct v4l2_standard *e = arg;
		if (e->index < 0 )
			return -EINVAL;
		if( e->index < dev->ext_vv_data->num_stds ) {
			DEB_EE(("VIDIOC_ENUMSTD: index:%d\n",e->index));
			v4l2_video_std_construct(e, dev->ext_vv_data->stds[e->index].id, dev->ext_vv_data->stds[e->index].name);
			return 0;
		}
		return -EINVAL;
	}
	case VIDIOC_S_STD:
	{
		v4l2_std_id *id = arg;
		int found = 0;
		int i, err;

		DEB_EE(("VIDIOC_S_STD\n"));

		if ((vv->video_status & STATUS_CAPTURE) == STATUS_CAPTURE) {
			DEB_D(("cannot change video standard while streaming capture is active\n"));
			return -EBUSY;
		}

		if ((vv->video_status & STATUS_OVERLAY) != 0) {
			vv->ov_suspend = vv->video_fh;
			err = saa7146_stop_preview(vv->video_fh); /* side effect: video_status is now 0, video_fh is NULL */
			if (0 != err) {
				DEB_D(("suspending video failed. aborting\n"));
				return err;
			}
		}

		down(&dev->lock);

		for(i = 0; i < dev->ext_vv_data->num_stds; i++)
			if (*id & dev->ext_vv_data->stds[i].id)
				break;
		if (i != dev->ext_vv_data->num_stds) {
			vv->standard = &dev->ext_vv_data->stds[i];
			if( NULL != dev->ext_vv_data->std_callback )
				dev->ext_vv_data->std_callback(dev, vv->standard);
			found = 1;
		}

		up(&dev->lock);

		if (vv->ov_suspend != NULL) {
			saa7146_start_preview(vv->ov_suspend);
			vv->ov_suspend = NULL;
		}

		if( 0 == found ) {
			DEB_EE(("VIDIOC_S_STD: standard not found.\n"));
			return -EINVAL;
		}

		DEB_EE(("VIDIOC_S_STD: set to standard to '%s'\n",vv->standard->name));
		return 0;
	}
	case VIDIOC_OVERLAY:




	{
		int on = *(int *)arg;
		int err = 0;

		DEB_D(("VIDIOC_OVERLAY on:%d\n",on));
		if (on != 0) {
			err = saa7146_start_preview(fh);
		} else {
			err = saa7146_stop_preview(fh);
		}
		return err;
	}
	case VIDIOC_REQBUFS: {
		struct v4l2_requestbuffers *req = arg;
		DEB_D(("VIDIOC_REQBUFS, type:%d\n",req->type));
		return videobuf_reqbufs(q,req);
	}
	case VIDIOC_QUERYBUF: {
		struct v4l2_buffer *buf = arg;
		DEB_D(("VIDIOC_QUERYBUF, type:%d, offset:%d\n",buf->type,buf->m.offset));
		return videobuf_querybuf(q,buf);
	}
	case VIDIOC_QBUF: {
		struct v4l2_buffer *buf = arg;
		int ret = 0;
		ret = videobuf_qbuf(q,buf);
		DEB_D(("VIDIOC_QBUF: ret:%d, index:%d\n",ret,buf->index));
		return ret;
	}
	case VIDIOC_DQBUF: {
		struct v4l2_buffer *buf = arg;
		int ret = 0;
		ret = videobuf_dqbuf(q,buf,file->f_flags & O_NONBLOCK);
		DEB_D(("VIDIOC_DQBUF: ret:%d, index:%d\n",ret,buf->index));
		return ret;
	}
	case VIDIOC_STREAMON: {
		int *type = arg;
		DEB_D(("VIDIOC_STREAMON, type:%d\n",*type));

		err = video_begin(fh);
		if( 0 != err) {
			return err;
		}
		err = videobuf_streamon(q);
		return err;
	}
	case VIDIOC_STREAMOFF: {
		int *type = arg;

		DEB_D(("VIDIOC_STREAMOFF, type:%d\n",*type));

		/* ugly: we need to copy some checks from video_end(),
		   because videobuf_streamoff() relies on the capture running.
		   check and fix this */
		if ((vv->video_status & STATUS_CAPTURE) != STATUS_CAPTURE) {
			DEB_S(("not capturing.\n"));
			return 0;
		}

		if (vv->video_fh != fh) {
			DEB_S(("capturing, but in another open.\n"));
			return -EBUSY;
		}

		err = videobuf_streamoff(q);
		if (0 != err) {
			DEB_D(("warning: videobuf_streamoff() failed.\n"));
			video_end(fh, file);
		} else {
			err = video_end(fh, file);
		}
		return err;
	}
	case VIDIOCGMBUF:
	{
		struct video_mbuf *mbuf = arg;
		struct videobuf_queue *q;
		int i;

		/* fixme: number of capture buffers and sizes for v4l apps */
		int gbuffers = 2;
		int gbufsize = 768*576*4;

		DEB_D(("VIDIOCGMBUF \n"));

		q = &fh->video_q;
		down(&q->lock);
		err = videobuf_mmap_setup(q,gbuffers,gbufsize,
					  V4L2_MEMORY_MMAP);
		if (err < 0) {
			up(&q->lock);
			return err;
		}
		memset(mbuf,0,sizeof(*mbuf));
		mbuf->frames = gbuffers;
		mbuf->size   = gbuffers * gbufsize;
		for (i = 0; i < gbuffers; i++)
			mbuf->offsets[i] = i * gbufsize;
		up(&q->lock);
		return 0;
	}
	default:
		return v4l_compat_translate_ioctl(inode,file,cmd,arg,
						  saa7146_video_do_ioctl);
	}
	return 0;
}
Exemplo n.º 10
0
Arquivo: tox.c Projeto: Boerde/uTox
void tox_message(uint8_t tox_message_id, uint16_t param1, uint16_t param2, void *data)
{
    switch(tox_message_id) {
    case DHT_CONNECTED: {
        /* param1: connection status (1 = connected, 0 = disconnected)
         */
        tox_connected = param1;
        redraw();
        break;
    }

    case DNS_RESULT: {
        /* param1: result (0 = failure, 1 = success)
         * data: resolved tox id (if successful)
         */
        if(param1) {
            friend_addid(data, edit_addmsg.data, edit_addmsg.length);
        } else {
            addfriend_status = ADDF_BADNAME;
        }
        free(data);

        redraw();
        break;
    }

    case OPEN_FILES: {
        tox_postmessage(TOX_SENDFILES, param1, param2, data);
        break;
    }

    case SAVE_FILE: {
        tox_postmessage(TOX_ACCEPTFILE, param1, param2, data);
        break;
    }

    case NEW_AUDIO_IN_DEVICE: {
        if(UI_STRING_ID_INVALID == param1) {
            list_dropdown_add_hardcoded(&dropdown_audio_in, data, data);
        } else {
            list_dropdown_add_localized(&dropdown_audio_in, param1, data);
        }

        if (loaded_audio_in_device != 0 && (dropdown_audio_in.dropcount - 1) == loaded_audio_in_device) {
            toxaudio_postmessage(AUDIO_SET_INPUT, 0, 0, data);
            dropdown_audio_in.selected = loaded_audio_in_device;
            loaded_audio_in_device = 0;
        }

        break;
    }

    case NEW_AUDIO_OUT_DEVICE: {
        list_dropdown_add_hardcoded(&dropdown_audio_out, data, data);

        if (loaded_audio_out_device != 0 && (dropdown_audio_out.dropcount - 1) == loaded_audio_out_device) {
            toxaudio_postmessage(AUDIO_SET_OUTPUT, 0, 0, data);
            dropdown_audio_out.selected = loaded_audio_out_device;
            loaded_audio_out_device = 0;
        }

        break;
    }

    case NEW_VIDEO_DEVICE: {
        if(UI_STRING_ID_INVALID == param1) {
            // Device name is a hardcoded string.
            // data is a pointer to a buffer, that contains device handle pointer,
            // followed by device name string.
            list_dropdown_add_hardcoded(&dropdown_video, data + sizeof(void*), *(void**)data);
        } else {
            // Device name is localized with param1 containing UI_STRING_ID.
            // data is device handle pointer.
            list_dropdown_add_localized(&dropdown_video, param1, data);
        }
        //param2 == true, if this device will be chosen by video detecting code.
        if(param2) {
            dropdown_video.selected = dropdown_video.over = (dropdown_video.dropcount - 1);
        }
        break;
    }

    case FRIEND_REQUEST: {
        /* data: pointer to FRIENDREQ structure
         */
        list_addfriendreq(data);
        break;
    }

    case FRIEND_ADD: {
        /* confirmation that friend has been added to friend list (add) */
        if(param1) {
            /* friend was not added */
            addfriend_status = param2;
        } else {
            /* friend was added */
            edit_addid.length = 0;
            edit_addmsg.length = 0;

            FRIEND *f = &friend[param2];
            friends++;

            f->msg.scroll = 1.0;

            memcpy(f->cid, data, sizeof(f->cid));

            friend_setname(f, NULL, 0);

            list_addfriend(f);

            addfriend_status = ADDF_SENT;

        }

        free(data);
        redraw();
        break;
    }

    case FRIEND_ACCEPT: {
        /* confirmation that friend has been added to friend list (accept) */
        if(!param1) {
            FRIEND *f = &friend[param2];
            FRIENDREQ *req = data;
            friends++;

            memcpy(f->cid, req->id, sizeof(f->cid));
            friend_setname(f, NULL, 0);

            list_addfriend2(f, req);
            redraw();
        }

        free(data);
        break;
    }

    case FRIEND_DEL: {
        friend_free(data);
        friends--;
        break;
    }

    case FRIEND_MESSAGE: {
        friend_addmessage(&friend[param1], data);
        redraw();
        break;
    }

#define updatefriend(fp) redraw();//list_draw(); if(sitem && fp == sitem->data) {ui_drawmain();}
#define updategroup(gp) redraw();//list_draw(); if(sitem && gp == sitem->data) {ui_drawmain();}

    case FRIEND_NAME: {
        FRIEND *f = &friend[param1];
        friend_setname(f, data, param2);
        updatefriend(f);
        break;
    }

    case FRIEND_STATUS_MESSAGE: {
        FRIEND *f = &friend[param1];
        free(f->status_message);
        f->status_length = param2;
        f->status_message = data;
        updatefriend(f);
        break;
    }

    case FRIEND_STATUS: {
        FRIEND *f = &friend[param1];
        f->status = param2;
        updatefriend(f);
        break;
    }

    case FRIEND_TYPING: {
        FRIEND *f = &friend[param1];
        friend_set_typing(f, param2);
        updatefriend(f);
        break;
    }

    case FRIEND_ONLINE: {
        FRIEND *f = &friend[param1];
        f->online = param2;
        if(!f->online) {
            friend_set_typing(f, 0);
        }
        updatefriend(f);
        break;
    }

    case FRIEND_CALL_STATUS: {
        /* param1: friend id
           param2: call id
           data: integer call status
         */
        FRIEND *f = &friend[param1];
        uint8_t status = (size_t)data;
        if(status == CALL_NONE && (f->calling == CALL_OK || f->calling == CALL_OK_VIDEO)) {
            toxaudio_postmessage(AUDIO_CALL_END, param2, 0, NULL);
            if(f->calling == CALL_OK_VIDEO) {
                toxvideo_postmessage(VIDEO_CALL_END, param2, 0, NULL);
            }

            video_end(param1 + 1);
        }

        f->calling = status;
        f->callid = param2;

        if(status == CALL_OK) {
            toxaudio_postmessage(AUDIO_CALL_START, param2, 0, NULL);
        }

        call_notify(f, status);

        updatefriend(f);
        break;
    }

    case FRIEND_CALL_VIDEO: {
        /* param1: friend id
           param2: call id
         */
        FRIEND *f = &friend[param1];
        f->calling = CALL_OK_VIDEO;
        f->callid = param2;
        updatefriend(f);

        toxvideo_postmessage(VIDEO_CALL_START, param2, 0, NULL);
        toxaudio_postmessage(AUDIO_CALL_START, param2, 0, NULL);

        f->call_width = 640;
        f->call_height = 480;

        video_begin(param1 + 1, f->name, f->name_length, 640, 480);

        call_notify(f, CALL_OK_VIDEO);

        break;
    }

    case FRIEND_CALL_MEDIACHANGE: {
        /* param1: friend id
           param2: call id
           data: zero = audio, nonzero = audio/video
         */
        FRIEND *f = &friend[param1];

        if(!data) {
            video_end(param1 + 1);
        } else {
            f->call_width = 640;
            f->call_height = 480;

            video_begin(param1 + 1, f->name, f->name_length, 640, 480);
        }
        break;
    }

    case FRIEND_CALL_START_VIDEO: {
        /* param1: friend id
           param2: call id
         */
        FRIEND *f = &friend[param1];
        if(f->calling == CALL_OK) {
            f->calling = CALL_OK_VIDEO;
            toxvideo_postmessage(VIDEO_CALL_START, param2, 0, NULL);
            updatefriend(f);
        }
        break;
    }

    case FRIEND_CALL_STOP_VIDEO: {
        /* param1: friend id
           param2: call id
         */
        FRIEND *f = &friend[param1];
        if(f->calling == CALL_OK_VIDEO) {
            f->calling = CALL_OK;
            toxvideo_postmessage(VIDEO_CALL_END, param2, 0, NULL);
            updatefriend(f);
        }
        break;
    }

    case FRIEND_VIDEO_FRAME: {
        /* param1: friend id
           param2: call id
           data: frame data
         */
        uint16_t *image = data;
        FRIEND *f = &friend[param1];

        _Bool b = (image[0] != f->call_width || image[1] != f->call_height);
        if(b) {
            f->call_width = image[0];
            f->call_height = image[1];
        }
        video_frame(param1 + 1, (void*)&image[2], image[0], image[1], b);
        free(image);
        break;
    }

    case PREVIEW_FRAME_NEW:
    case PREVIEW_FRAME: {
        if(video_preview) {
            video_frame(0, data, param1, param2, tox_message_id == PREVIEW_FRAME_NEW);
        }
        free(data);
        break;
    }

    case FRIEND_FILE_IN_NEW:
    case FRIEND_FILE_IN_NEW_INLINE: {
        FRIEND *f = &friend[param1];
        FILE_T *ft = &f->incoming[param2];
        _Bool inline_png = (tox_message_id == FRIEND_FILE_IN_NEW_INLINE);

        MSG_FILE *msg = malloc(sizeof(MSG_FILE));
        msg->author = 0;
        msg->msg_type = MSG_TYPE_FILE;
        msg->filenumber = param2;
        msg->status = (inline_png ? FILE_OK : FILE_PENDING);
        msg->name_length = (ft->name_length > sizeof(msg->name)) ? sizeof(msg->name) : ft->name_length;
        msg->size = ft->total;
        msg->progress = 0;
        msg->speed = 0;
        msg->inline_png = inline_png;
        msg->path = NULL;
        memcpy(msg->name, ft->name, msg->name_length);

        friend_addmessage(f, msg);
        ft->chatdata = msg;

        file_notify(f, msg);

        updatefriend(f);
        break;
    }

    case FRIEND_FILE_OUT_NEW:
    case FRIEND_FILE_OUT_NEW_INLINE: {
        FRIEND *f = &friend[param1];
        FILE_T *ft = &f->outgoing[param2];
        _Bool inline_png = (tox_message_id == FRIEND_FILE_OUT_NEW_INLINE);

        MSG_FILE *msg = malloc(sizeof(MSG_FILE));
        msg->author = 1;
        msg->msg_type = MSG_TYPE_FILE;
        msg->filenumber = param2;
        msg->status = FILE_PENDING;
        msg->name_length = (ft->name_length >= sizeof(msg->name)) ? sizeof(msg->name) - 1 : ft->name_length;
        msg->size = ft->total;
        msg->progress = 0;
        msg->speed = 0;
        msg->inline_png = inline_png;
        msg->path = NULL;
        memcpy(msg->name, ft->name, msg->name_length);
        msg->name[msg->name_length] = 0;

        friend_addmessage(f, msg);
        ft->chatdata = msg;

        updatefriend(f);
        break;
    }

    case FRIEND_FILE_IN_STATUS: {
        FRIEND *f = &friend[param1];
        FILE_T *ft = &f->incoming[param2];

        MSG_FILE *msg = ft->chatdata;
        msg->status = (size_t)data;

        file_notify(f, msg);

        updatefriend(f);
        break;
    }

    case FRIEND_FILE_OUT_STATUS: {
        FRIEND *f = &friend[param1];
        FILE_T *ft = &f->outgoing[param2];

        MSG_FILE *msg = ft->chatdata;
        msg->status = (size_t)data;

        file_notify(f, msg);

        updatefriend(f);
        break;
    }

    case FRIEND_FILE_IN_DONE: {
        FRIEND *f = &friend[param1];
        FILE_T *ft = &f->incoming[param2];

        MSG_FILE *msg = ft->chatdata;
        msg->status = FILE_DONE;
        msg->path = data;

        file_notify(f, msg);

        updatefriend(f);
        break;
    }

    case FRIEND_FILE_IN_DONE_INLINE: {
        FRIEND *f = &friend[param1];
        FILE_T *ft = &f->incoming[param2];

        MSG_FILE *msg = ft->chatdata;
        msg->status = FILE_DONE;
        msg->path = data;

        friend_recvimage(f, data, msg->size);

        file_notify(f, msg);

        updatefriend(f);
        break;
    }

    case FRIEND_FILE_OUT_DONE: {
        FRIEND *f = &friend[param1];
        FILE_T *ft = &f->outgoing[param2];

        MSG_FILE *msg = ft->chatdata;
        msg->status = FILE_DONE;
        msg->path = data;

        file_notify(f, msg);

        updatefriend(f);
        break;
    }

    case FRIEND_FILE_IN_PROGRESS: {
        FRIEND *f = &friend[param1];
        FILE_T *ft = &f->incoming[param2];
        FILE_PROGRESS *p = data;

        MSG_FILE *msg = ft->chatdata;
        msg->progress = p->bytes;
        msg->speed = p->speed;

        free(p);

        updatefriend(f);
        break;
    }

    case FRIEND_FILE_OUT_PROGRESS: {
        FRIEND *f = &friend[param1];
        FILE_T *ft = &f->outgoing[param2];
        FILE_PROGRESS *p = data;

        MSG_FILE *msg = ft->chatdata;
        msg->progress = p->bytes;
        msg->speed = p->speed;

        free(p);

        updatefriend(f);
        break;
    }

    case GROUP_ADD: {
        GROUPCHAT *g = &group[param1];
        g->name_length = snprintf((char*)g->name, sizeof(g->name), "Groupchat #%u", param1);
        g->topic_length = sizeof("Drag friends to invite them") - 1;
        memcpy(g->topic, "Drag friends to invite them", sizeof("Drag friends to invite them") - 1);
        g->msg.scroll = 1.0;
        g->type = tox_group_get_type(data, param1);
        list_addgroup(g);
        redraw();
        break;
    }

    case GROUP_MESSAGE: {
        GROUPCHAT *g = &group[param1];

        message_add(&messages_group, data, &g->msg);

        if(sitem && g == sitem->data) {
            redraw();//ui_drawmain();
        }

        break;
    }

    case GROUP_PEER_DEL: {
        GROUPCHAT *g = &group[param1];

        if (param2 > MAX_GROUP_PEERS) //TODO: dynamic arrays.
            break;

        if(g->peername[param2]) {
            free(g->peername[param2]);
            g->peername[param2] = NULL;
        }

        g->peers--;
        g->peername[param2] = g->peername[g->peers];
        g->peername[g->peers] = NULL;

        if (g->type == TOX_GROUPCHAT_TYPE_AV) {
            g->last_recv_audio[param2] = g->last_recv_audio[g->peers];
            g->last_recv_audio[g->peers] = 0;
            group_av_peer_remove(g, param2);
            g->source[param2] = g->source[g->peers];
        }

        if (g->peers == g->our_peer_number) {
            g->our_peer_number = param2;
        }

        g->topic_length = snprintf((char*)g->topic, sizeof(g->topic), "%u users in chat", g->peers);

        updategroup(g);

        break;
    }

    case GROUP_PEER_ADD:
    case GROUP_PEER_NAME: {
        GROUPCHAT *g = &group[param1];

        if (param2 > MAX_GROUP_PEERS) //TODO: dynamic arrays.
            break;

        if(g->peername[param2]) {
            free(g->peername[param2]);
        }

        if(tox_message_id == GROUP_PEER_ADD) {
            if (g->type == TOX_GROUPCHAT_TYPE_AV) {
                group_av_peer_add(g, param2);
            }

            if (tox_group_peernumber_is_ours(data, param1, param2)) {
                g->our_peer_number = param2;
            }

            uint8_t *n = malloc(10);
            n[0] = 9;
            memcpy(n + 1, "<unknown>", 9);
            data = n;
            g->peers++;

        }

        g->peername[param2] = data;

        g->topic_length = snprintf((char*)g->topic, sizeof(g->topic), "%u users in chat", g->peers);

        updategroup(g);

        break;
    }

    case GROUP_TITLE: {
        GROUPCHAT *g = &group[param1];

        if (param2 > sizeof(g->name)) {
            memcpy(g->name, data, sizeof(g->name));
            g->name_length = sizeof(g->name);
        } else {
            memcpy(g->name, data, param2);
            g->name_length = param2;
        }

        free(data);
        updategroup(g);
        break;
    }

    case GROUP_AUDIO_START: {
        GROUPCHAT *g = &group[param1];

        if (g->type == TOX_GROUPCHAT_TYPE_AV) {
            g->audio_calling = 1;
            toxaudio_postmessage(GROUP_AUDIO_CALL_START, param1, 0, NULL);
            updategroup(g);
        }
        break;
    }
    case GROUP_AUDIO_END: {
        GROUPCHAT *g = &group[param1];

        if (g->type == TOX_GROUPCHAT_TYPE_AV) {
            g->audio_calling = 0;
            toxaudio_postmessage(GROUP_AUDIO_CALL_END, param1, 0, NULL);
            updategroup(g);
        }
        break;
    }

    case GROUP_UPDATE: {
        GROUPCHAT *g = &group[param1];
        updategroup(g);

        break;
    }
    }