I realise this is an old question however I found it through a search engine so the solution I ended up with could be useful to others.
I took a slightly different approach and used jQuery UI Draggable, the same way external events are set up.
An event is copied when the user drags an event with the ctrl key held down, this leaves the original event in place and creates a new event where the jQuery draggable is dropped.
// used to track whether the user is holding the control key
let ctrlIsPressed = false;
function setEventsCopyable(isCopyable) {
ctrlIsPressed = !ctrlIsPressed;
$("#calendar").fullCalendar("option", "eventStartEditable", !isCopyable);
$(".fc-event").draggable("option", "disabled", !isCopyable);
}
// set events copyable if the user is holding the control key
$(document).keydown(function(e) {
if (e.ctrlKey && !ctrlIsPressed) {
setEventsCopyable(true);
}
});
// if control has been released stop events being copyable
$(document).keyup(function(e) {
if (ctrlIsPressed) {
setEventsCopyable(false);
}
});
$("#calendar").fullCalendar({
defaultView: "basicWeek",
events: [
{
title: "Event 1",
start: moment(),
end: moment().add(1, "d")
},
{
title: "Event 2",
start: moment().add(1, "d"),
end: moment().add(2, "d")
}
],
editable: true,
droppable: true,
eventAfterAllRender(event, element, view) {
// make all events draggable but disable dragging
$(".fc-event").each(function() {
const $event = $(this);
// store data so the calendar knows to render an event upon drop
const event = $event.data("fcSeg").footprint.eventDef;
$event.data("event", event);
// make the event draggable using jQuery UI
$event.draggable({
disabled: true,
helper: "clone",
revert: true,
revertDuration: 0,
zIndex: 999,
stop(event, ui) {
// when dragging of a copied event stops we must set them
// copyable again if the control key is still held down
if (ctrlIsPressed) {
setEventsCopyable(true);
}
}
});
});
}
});
Working Codepen.